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
source to share
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.
source to share