Общий интерфейс, наследующий Non-Generic One С#
Это вопрос дизайна класса.
У меня есть основной абстрактный класс
public abstract class AbstractBlockRule
{
public long Id{get;set;}
public abstract List<IRestriction> Restrictions {get;};
}
public interface IRestriction{}
public interface IRestriction<T>:IRestriction where T:struct
{
T Limit {get;}
}
public TimeRestriction:IRestriction<TimeSpan>
{
public TimeSpan Limit{get;set;}
}
public AgeRestriction:IRestriction<int>
{
public int Limit{get;set;}
}
public class BlockRule:AbstractBlockRule
{
public virtual List<IRestriction> Restrictions {get;set;}
}
BlockRule rule=new BlockRule();
TimeRestriction t=new TimeRestriction();
AgeRestriction a=new AgeRestriction();
rule.Restrictions.Add(t);
rule.Restrictions.Add(a);
Мне нужно использовать не общий интерфейс IRestriction, чтобы избежать указания общего типа T в основном абстрактном классе. Я очень новичок в дженериках. Может кто-нибудь дать мне знать, как лучше спроектировать эту вещь?
Ответы
Ответ 1
Ваш подход типичен (например, IEnumerable <T> реализует IEnumerable, как это). Если вы хотите предоставить максимальную полезность потребителям вашего кода, было бы неплохо предоставить не общий приемник на не-общий интерфейс, а затем скрыть его в общей реализации. Например:
public abstract class AbstractBlockRule
{
public long Id{get;set;}
public abstract List<IRestriction> Restrictions { get; set; }
}
public interface IRestriction
{
object Limit { get; }
}
public interface IRestriction<T> : IRestriction
where T:struct
{
// hide IRestriction.Limit
new T Limit {get;}
}
public abstract class RestrictionBase<T> : IRestriction<T>
where T:struct
{
// explicit implementation
object IRestriction.Limit
{
get { return Limit; }
}
// override when required
public virtual T Limit { get; set; }
}
public class TimeRestriction : RestrictionBase<TimeSpan>
{
}
public class AgeRestriction : RestrictionBase<TimeSpan>
{
}
public class BlockRule : AbstractBlockRule
{
public override List<IRestriction> Restrictions { get; set; }
}
Я также показал, что здесь используется базовый класс ограничения, но он не требуется.
Ответ 2
Среда выполнения рассматривает IRestriction<TimeSpan>
и IRestriction<int>
как разные разные классы (у них даже есть свой собственный набор статических переменных). В вашем случае единственными классами, общими для IRestriction<TimeSpan>
и IRestriction<int>
в иерархии наследования, являются IRestriction
и object
.
И действительно, наличие списка IRestriction
- единственный разумный путь.
В качестве побочного примечания: у вас есть свойство Limit
, в котором вы можете получить доступ, независимо от того, имеете ли вы дело с IRestriction<TimeSpan>
или IRestriction<int>
. В этом случае я хотел бы определить другое свойство object Limit { get; }
на IRestriction
и скрыть его в реальной реализации. Вот так:
public interface IRestriction
{
object Limit { get; }
}
public interface IRestriction<T> : IRestriction
where T : struct
{
new T Limit { get; set; }
}
public class TimeRestriction : IRestriction<TimeSpan>
{
public TimeSpan Limit { get; set; }
// Explicit interface member:
// This is hidden from IntelliSense
// unless you cast to IRestriction.
object IRestriction.Limit
{
get
{
// Note: boxing happens here.
return (object)Limit;
}
}
}
Таким образом вы можете получить доступ к Limit
как object
во всех своих IRestriction
, когда вам все равно, какой тип он есть. Например:
foreach(IRestriction restriction in this.Restrictions)
{
Console.WriteLine(restriction.Limit);
}
Ответ 3
Интерфейсы - это контракты, за которыми должен следовать объект, который реализует контракт.
Вы создали два контракта с тем же именем IRestriction
Насколько я могу судить, вам может понадобиться флаг для классов, которые могут быть ограничены, что должно реализовать нестандартный интерфейс IRestriction
.
Второй интерфейс, по-видимому, является ограничивающим объектом, который также содержит свойство limit.
Следовательно, определение второго интерфейса IRestriction
может быть ILimitRestriction
или любое другое имя соответствует потребностям вашего бизнеса.
Следовательно, ILimitRestriction
может наследовать от IRestriction
, который будет отмечать классы, наследующие ILimitRestriction
неподвижные объекты IRestriction
public abstract class AbstractBlockRule
{
public long Id{get;set;}
public abstract List<IRestriction> Restrictions {get;};
}
public interface IRestriction{}
public interface IRestrictionWithLimit<T>:IRestriction where T:struct
{
T Limit {get;}
}
public TimeRestriction:IRestrictionWithLimit<TimeSpan>
{
public TimeSpan Limit{get;set;}
}
public AgeRestriction:IRestrictionWithLimit<int>
{
public int Limit{get;set;}
}
public class BlockRule:AbstractBlockRule
{
public virtual List<IRestriction> Restrictions {get;set;}
}