Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Generic Interface inheriting Non-Generic One C#

Tags:

This is class design question.

I have main abstract class

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

I have to use non-generic Interface IRestriction just to avoid specifying generic type T in main abstract class. I'm very new to generics. Can some one let me know how to better design this thing?

like image 1000
callee.args Avatar asked Jan 18 '14 10:01

callee.args


People also ask

Can a non-generic class inherit a generic class?

Yes you can do it.

Can a generic implement an interface?

Only generic classes can implement generic interfaces. Normal classes can't implement generic interfaces.

What is a generic interface C#?

A generic interface is primarily a normal interface like any other. It can be used to declare a variable but assigned the appropriate class. It can be returned from a method. It can be passed as argument. You pass a generic interface primarily the same way you would an interface.

Can we inherit generic class in C#?

You cannot inherit a generic type. // class Derived20 : T {}// NO!


2 Answers

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.

like image 192
Jack Avatar answered Oct 25 '22 23:10

Jack


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); } 
like image 32
Daniel A.A. Pelsmaeker Avatar answered Oct 26 '22 01:10

Daniel A.A. Pelsmaeker