Casting to a generic class with interface

家住魔仙堡 提交于 2019-12-10 19:46:16

问题


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!