问题
Updated with input from MarcinJuraszek
I have a feeling I'm bumping into a co / contra variance problem here, but I'm not sure I understand how to fix it. 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 SomeClass property:
public class MyContainer<TEnum> where TEnum : struct, IConvertible
{
public SomeClass<TEnum, IMyInterface> MyProp { get; set; }
}
Now my problem is that I can't assign a SomeClass<MyEnum, MyImplementation> to the MyProp property because I get an InvalidCastException at runtime complaining that it can't cast the SomeClass<MyEnum, MyImplementation> to SomeClass<MyEnum, IMyInterface>.
How do I work around this?
Example, this doesn't compile:
var c = new MyContainer<MyEnum>();
c.MyProp = new SomeClass<MyEnum, MyImplementation>();
Here's a dot net fiddle
回答1:
You can make it work by having your generic type parameter invariant (either covariant or contravariant, depending on its members). However, in C# you can only declare generic parameters invariant on interface, so you'd have to declare another 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; }
}
That would make following code compile:
var container = new MyContainer<DayOfWeek>();
container.MyProp = new SomeClass<DayOfWeek, MyImplementation>();
Another possible solution would be to use another interface, where S generic type parameter doesn't 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 - as of why it doesn't work:
Let's imagine that your code compiles, and you can assign MyClass<T> to MyClass<IT> as long as T implements IT. You could have following class:
class MyClass<T>
{
public List<T> MyProp { get; set; }
}
And do
MyClass<IMyInterface> instance = new MyClass<MyInterfaceImplementation>();
with that instance.MyProp would be List<MyInterfaceImplementation> but you had access to it as if it was List<IMyInterface> so you could try adding element of MyOtherInterfaceImplementation which would crash at runtime. Not fun.
来源:https://stackoverflow.com/questions/27910861/casting-to-a-generic-class-with-interface