Подтвердить что ты не робот

Общий интерфейс, наследующий 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 в основном абстрактном классе. Я очень новичок в дженериках. Может кто-нибудь дать мне знать, как лучше спроектировать эту вещь?

4b9b3361

Ответ 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;}
}