Declaring a variable of a common base type when inherited types have different type parameters

I have a class hierarchy, something like this:

public abstract class BaseDecision { // <implementation> } public class CCDecision : BaseDecision { // <implementation> } public class UCDecision : BaseDecision { // <implementation> } public abstract class BaseInfo<TDecision> where TDecision:BaseDecision, new() { public TDecision proposedDecision; public TDecision finalDecision; // <implementation> } public class CCInfo : BaseInfo<CCDecision> { // <implementation> } public class UCInfo : BaseInfo<UCDecision> { // <implementation> } 

The problem is that with such a class hierarchy, I cannot declare a variable that can contain instances of the CCInfo and UCInfo classes (since they use the base type with a different type of parameters). As far as I understand, I also can not use dispersion, as my general parameter is used for both input and output.

I personally feel some kind of anti-pattern here, but just can't figure out how to solve this.

+4
source share
2 answers

Design really depends on the goal you want to achieve. If you want one variable to be able to store instances of the CCInfo and UCInfo classes, this variable can only see what is common to these two types. It seems to me that the only possible one, which would be the same as CCInfo or UCInfo, will have access to the proposed Decision and finalDecision solution, is considered as BaseDecision instances (not more accurately if you want to keep the general character). Thus, in this context, these properties are read ("from properties"). Thus, you can rely on covariance as follows:

 class Program { public static void Main(string[] args) { CCInfo ccInfo = new CCInfo(); UCInfo ucInfo = new UCInfo(); IBaseInfo<BaseDecision> x = ccInfo; x = ucInfo; } public class BaseDecision { // <implementation> } public class CCDecision : BaseDecision { // <implementation> } public class UCDecision : BaseDecision { // <implementation> } public interface IBaseInfo<out TDecision> where TDecision : BaseDecision, new() { TDecision proposedDecision { get; } TDecision finalDecision { get; } } public abstract class BaseInfo<TDecision> : IBaseInfo<TDecision> where TDecision : BaseDecision, new() { public TDecision proposedDecision { get; set; } public TDecision finalDecision { get; set; } // <implementation> } public class CCInfo : BaseInfo<CCDecision> { // <implementation> } public class UCInfo : BaseInfo<UCDecision> { // <implementation> } } 

Of course it compiles. Now it's up to you to use this piece of code in your specific context to achieve your goals ... Good luck!

+2
source

You can either make a non-generic base class or an interface, or use a covariant generic interface (which requires properties to be read-only).

+4
source

All Articles