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?
Yes you can do it.
Only generic classes can implement generic interfaces. Normal classes can't implement generic interfaces.
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.
You cannot inherit a generic type. // class Derived20 : T {}// NO!
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.
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); }
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With