Casting to a generic class with an interface

Updated with material from MarcinJuraszek

I have a feeling that I am here in a combined variable problem, but I'm not sure I understand how to fix this. I have a class like this:

public interface ISomeClass<TEnum, out S>
{
     TEnum Dim { get; }
     IEnumerable<S> Inc { get; }
}

public class SomeClass<TEnum, S> : ISomeClass<TEnum, S>
    where TEnum : struct, IConvertible
    where S : IMyInterface
{
    public TEnum Dim { get; set; }
    public IEnumerable<S> Inc { get; set; }
}

      

and I have a class that implements IMyInterface

public class MyImplementation : IMyInterface
{

}

      

And of course I have a class with a property SomeClass

:

public class MyContainer<TEnum> where TEnum : struct, IConvertible
{
    public SomeClass<TEnum, IMyInterface> MyProp { get; set; }
}

      

Now my problem is that I cannot assign to the SomeClass<MyEnum, MyImplementation>

property MyProp

because at runtime I get InvalidCastException

complaining that it cannot cast SomeClass<MyEnum, MyImplementation>

in SomeClass<MyEnum, IMyInterface>

.

How do I get around this?

Example: this doesn't compile:

var c = new MyContainer<MyEnum>();
c.MyProp = new SomeClass<MyEnum, MyImplementation>();

      

Here's the dot net fiddle

+3


source to share


1 answer


You can make it work if your generic type parameter is invariant (either covariant or contravariant, depending on its members). However, in C # you can declare general parameters that are invariant across an interface, so you have to declare a different interface:

public interface ISomeClass<TEnum, in S>
{

}

public class SomeClass<TEnum, S> : ISomeClass<TEnum, IMyInterface>
    where TEnum : struct, IConvertible
    where S : IMyInterface
{

}

public class MyContainer<TEnum> where TEnum : struct, IConvertible
{
    public ISomeClass<TEnum, IMyInterface> MyProp { get; set; }
}

      

The following code will do it:

var container = new MyContainer<DayOfWeek>();
container.MyProp = new SomeClass<DayOfWeek, MyImplementation>();

      

Another possible solution would be to use a different interface where S

the type parameter does not exist:

public interface ISomeClass<TEnum>
    where TEnum: struct, IConvertible
{

}

public class SomeClass<TEnum, S> : ISomeClass<TEnum>
    where TEnum : struct, IConvertible
    where S : IMyInterface
{

}

public class MyContainer<TEnum> where TEnum : struct, IConvertible
{
    public ISomeClass<TEnum> MyProp { get; set; }
}

      



Bonus - due to why it doesn't work:

Imagine your code is compiling and you can assign MyClass<T>

to MyClass<IT>

while T

implementing IT

. You might have the following class:

class MyClass<T>
{
    public List<T> MyProp { get; set; }
}

      

And do

MyClass<IMyInterface> instance = new MyClass<MyInterfaceImplementation>();

      

with what instance.MyProp

would be List<MyInterfaceImplementation>

, but you had access to it as if you were List<IMyInterface>

, so you could try adding an element MyOtherInterfaceImplementation

that will crash at runtime. Not fun.

+4


source







All Articles