Generic Interface inheriting Non-Generic One C#

前端 未结 3 1356
无人及你
无人及你 2020-12-09 16:18

This is class design question.

I have main abstract class

public abstract class AbstractBlockRule
{
    public long Id{get;set;}
    public abstract         


        
相关标签:
3条回答
  • 2020-12-09 16:42

    Your approach is typical (for example, IEnumerable<T> implements IEnumerable like this). If you want to provide maximum utility to consumers of your code, it would be nice to provide a non-generic accessor on the non-generic interface, then hide it in the generic implementation. For example:

    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; }
    }
    

    I also showed using a base restriction class here, but it is not required.

    0 讨论(0)
  • 2020-12-09 16:45

    The runtime treats IRestriction<TimeSpan> and IRestriction<int> as different distinct classes (they even have their own set of static variables). In your case the only classes common to both IRestriction<TimeSpan> and IRestriction<int> in the inheritance hierarchy are IRestriction and object.

    So indeed, having a list of IRestriction is the only sensible way to go.


    As a side note: you have a property Limit in there that you might want to access regardless of whether you're dealing with an IRestriction<TimeSpan> or IRestriction<int>. What I would do in this case is to define another property object Limit { get; } on IRestriction, and hide it in the actual implementation. Like this:

    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;
            }
        }
    }
    

    This way you can access Limit as object on all your IRestriction when you don't care what type it is. For example:

    foreach(IRestriction restriction in this.Restrictions)
    {
        Console.WriteLine(restriction.Limit);
    }
    
    0 讨论(0)
  • 2020-12-09 16:50

    Interfaces are contracts that need to be followed by the entity that implements the contract.

    You have created two contract with the same name IRestriction

    As far as I can see, what you are basically may need is a flag for classes that can be restricted, which should implement the IRestriction non-generic interface.

    The second interface seems to be restrictable objects that also contain a limit property. Hence the definition of the second IRestriction interface can be ILimitRestriction or whatever name suits your business needs.

    Hence ILimitRestriction can inherit from IRestriction which would mark classes inheriting ILimitRestriction still objects of 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;}
    }
    
    0 讨论(0)
提交回复
热议问题