1

Hy all, So I just want to work with generic and casting. But for some reason is does not work and I do not know why.

abstract class BaseModel {}

    class NewModel : BaseModel {}

    class BaseRepo<T> where T : BaseModel {}

    class NewRepo : BaseRepo<NewModel> {}

    class Test
    {
        public void TestMethod()
        {
            BaseRepo<BaseModel> t1 = new BaseRepo<BaseModel>();
            BaseRepo<NewModel> t2 = new NewRepo();
            BaseRepo<BaseModel> t3 = new BaseRepo<NewModel>();
            // Cannot convert initializer type 'TestGeneric.BaseRepo<TestGeneric.NewModel> to target type 'TestGeneric.BaseRepo<BaseModel>'.
            // Cannot implicitly convert type 'TestGeneric.BaseRepo<TestGeneric.NewModel>' to 'TestGeneric.BaseRepo<TestGeneric.BaseModel>'.
            // Type 'NewModel' doesn't match expected type 'BaseModel'.
            // Cannot convert source type 'TestGeneric.BaseRepo<TestGeneric.NewModel>' to target type 'TestGeneric.BaseRepo<TestGeneric.BaseModel>'.

            BaseRepo<BaseModel> t4 = new NewRepo();
            // Cannot convert initializer type 'TestGeneric.BaseRepo<TestGeneric.NewModel> to target type 'TestGeneric.BaseRepo<BaseModel>'.
            // Cannot implicitly convert type 'TestGeneric.NewRepo' to 'TestGeneric.BaseRepo<TestGeneric.BaseModel>'.
            // Cannot convert source type 'TestGeneric.NewRepo' to target type 'TestGeneric.BaseRepo<TestGeneric.BaseModel>'.
        }
    }

I just do not understand why t3 and t4 throwing does exceptions meanwhile t1 and t2 works. Even is NewModel is a subclass of BaseModel the last two does not works.

TylerH
  • 20,799
  • 66
  • 75
  • 101
  • Check the duplicate - particularly my [answer](https://stackoverflow.com/a/56573281/259769). I think it makes it easy to understand. – Enigmativity Jul 27 '19 at 06:43

1 Answers1

0

This is due to covariance feature of Generics:

As per Microsoft documentation about this tpoic:

Covariance and contravariance are terms that refer to the ability to use a more derived type (more specific) or a less derived type (less specific) than originally specified. Generic type parameters support covariance and contravariance to provide greater flexibility in assigning and using generic types. When you are referring to a type system, covariance, contravariance, and invariance have the following definitions. The examples assume a base class named Base and a derived class named Derived.

Covariance

Enables you to use a more derived type than originally specified.

You can assign an instance of IEnumerable (IEnumerable(Of Derived) in Visual Basic) to a variable of type IEnumerable.

Contravariance

Enables you to use a more generic (less derived) type than originally specified.

You can assign an instance of Action (Action(Of Base) in Visual Basic) to a variable of type Action.

Invariance

Means that you can use only the type originally specified; so an invariant generic type parameter is neither covariant nor contravariant.

You cannot assign an instance of List (List(Of Base) in Visual Basic) to a variable of type List or vice versa.

Covariant type parameters enable you to make assignments that look much like ordinary Polymorphism, as shown in the following code.

C# 

IEnumerable<Derived> d = new List<Derived>(); IEnumerable<Base> b=d;

So, you can use interfaces, eg change your code to the following:

Create an ICovariant interface and use it instead of base class BaseRepo

public interface ICovariant<out T> where T : BaseModel
{

}

Then change your BaseRepo like below:

public class BaseRepo<T> : ICovariant<T> where T : BaseModel { }

Now you can be able to do this:

BaseRepo<BaseModel> t1 = new BaseRepo<BaseModel>();
BaseRepo<NewModel> t2 = new NewRepo();
ICovariant<BaseModel> t3 = new BaseRepo<NewModel>();
ICovariant<BaseModel> t4 = new NewRepo();

The hole code below:

public interface ICovariant<out T> where T : BaseModel
{

}

public abstract class BaseModel { }

public class NewModel : BaseModel { }

public class BaseRepo<T> : ICovariant<T> where T : BaseModel { }

class NewRepo : BaseRepo<NewModel> { }

class Test
{
    public void TestMethod()
    {
        BaseRepo<BaseModel> t1 = new BaseRepo<BaseModel>();
        BaseRepo<NewModel> t2 = new NewRepo();
        ICovariant<BaseModel> t3 = new BaseRepo<NewModel>();
        ICovariant<BaseModel> t4 = new NewRepo();
    }
}

Covariance and Contravariance in Generics

Variance in Generic Interfaces (C#)

Mselmi Ali
  • 1,139
  • 2
  • 18
  • 28