Nick.T
Nick.T

Reputation: 577

Typecasting derived object to base object with generics

I have a covariant/contravariant issue with generics. Here's a code of what I'm trying to do :

in framework DLL:

namespace framework {
    public interface IBase 
    {
        //Some code...
    }

    public abstract class AOverClass<T> : IBase where T : IBase
    {
        public AOverClass(T pObject)
        {
            //Some code
        }
        //Some code...
    }

    public class workerClass 
    {
        public Dictionary<Type, Type> ObjDictionary;
        public AOverClass<IBase> GetOverObj(IBase initObj){

            // Get the over Obj type from a dictionary that was loaded previously
            // the dictionary contains KVP of <Type, Type> then using Reflection 
            // to initialise a new OverObj

            Type t = Dictionary[initObj.GetType()];
            Type[] _typesParamsConstructor = new Type[1];
            _typesParamsConstructor[0] = initObj.GetType();
            Object[] _valParamsConstructor = new Object[1];
            _valParamsConstructor [0] = initObj;

            // BIG ISSUE HERE

            return (AOverClass<IBase>)t.GetConstructor(_typesParamsConstructor).Invoke(_valParamsConstructor);
        }
    }
}

in application that references the framework DLL:

namespace myApp {
    public class Param : framework.IBase, myApp.IOtherNeeded
    {
        //Some code
    }

    public class OverParam : framework.AOverClass<Param>
    {
        public OverParam(Param pObject) :base(pObject)
        {
            //Some code...
        }
    }

    public class App
    {
        private framework.workerClass _wc;
        public void Init()
        {
            _wc = new framework.workerClass();
            _wc.ObjDictionary.Add(typeof(Param), typeof(OverParam));
        }

        public void Run()
        {
            _wc.GetOverObj(new Param());
        }
    }
}

The workerClass throws an Exception that it can't cast OverParam to AOverClass<IBase>. How can I work round this?

Foot note : There are no initialisation issues (i.e. the dictionary), I'm just no writting all the code that is not relevent to the issue.

Upvotes: 0

Views: 72

Answers (1)

Jeppe Stig Nielsen
Jeppe Stig Nielsen

Reputation: 62002

An instance of OverParam is an AOverClass<Param> by definition.

But you are trying to cast that instance of OverParam to AOverClass<IBase>. That fails.

Now just because any

Param "is" IBase

can we or can we not conclude that every

AOverClass<Param> "is" AOverClass<IBase>

?

The answer is: That works precisely if AOverClass<T> is covariant in T. The only types that can be covariant (marked with out before the type parameter T) in the current version of C# and .NET, are interface types and delegate types. But AOverClass<T> is a class type.

Also, when we look at your class:

public abstract class AOverClass<T> : IBase where T : IBase
{
    public AOverClass(T pObject)  // an "in" parameter of type T here! :-(
    {
        //Some code
    }
    //Some code...
}

we see that you use T contravariantly. So even if the language allowed class types to be covariant, it wouldn't apply in your case. This indicates that there is something fundamentally wrong with your assumptions.

Upvotes: 2

Related Questions