2014-01-18 2 views
16

Это вопрос дизайна класса.Общий интерфейс, наследующий Non-Generic One C#

У меня есть абстрактный класс Основные

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

Трудно улучшить код для меня.Неограниченный базовый интерфейс мог бы иметь свойство 'ValueType BoxedLimit {get;}', если бы вы хотели, но я не уверен, что это полезно. В конце концов, зачем вам «Список <>» объектов разных типов? –

+0

(Разумеется, очевидная идея сделать общий ковариантный интерфейс, 'IRestriction ', не полезна, когда вы используете его со значениями типов (structs), поскольку C# и .NET не поддерживают ковариацию со значениями типов.) –

+0

I Не знаю, почему вы не делаете ни абстрактный класс, ни метод, общий. Является ли 'Limit' минимальным или максимальным? Если так, возможно, «IComparable» был бы более полезен. –

ответ

12

Ваш подход типичен (например, 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; } 
} 

Я также показал здесь, используя базовый класс ограничения, но это не требуется.

0

Интерфейсы - это контракты, за которыми следует компания, которая реализует контракт.

Вы создали два контракта с тем же именем IRestriction

Насколько я могу видеть, что вы в принципе может понадобиться это флаг для классов, которые могут быть ограничены, которые должны осуществлять без универсального интерфейса IRestriction ,

Второй интерфейс, по-видимому, является ограничивающим объектом, который также содержит свойство предела. Следовательно, определение второго интерфейса 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;} 
} 
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); 
} 
Смежные вопросы