强制转换为具有接口的泛型类

本文关键字:接口 泛型类 转换 | 更新日期: 2023-09-27 18:37:19

根据 MarcinJuraszek 的输入进行了更新

有一种感觉,我在这里遇到了一个co/contra方差问题,但我不确定我是否知道如何解决它。我有这样的课程:

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; }
}

我有一个实现IMyInterface的类

public class MyImplementation : IMyInterface
{
}

而且,当然,我有一个具有SomeClass属性的类:

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

现在我的问题是我无法为 MyProp 属性分配SomeClass<MyEnum, MyImplementation>,因为我在运行时收到一个InvalidCastException,抱怨它无法将SomeClass<MyEnum, MyImplementation>转换为SomeClass<MyEnum, IMyInterface>

我该如何解决此问题?

例如,这不会编译:

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

这是一个点网小提琴

强制转换为具有接口的泛型类

您可以通过使泛型类型参数不变(协变或逆变,取决于其成员)来使其工作。但是,在 C# 中,只能在接口上声明泛型参数不变,因此必须声明另一个接口:

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; }
}

这将使以下代码编译:

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

另一种可能的解决方案是使用另一个接口,其中S泛型类型参数不存在:

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; }
}

奖金 - 至于为什么它不起作用:

假设您的代码可以编译,只要T实现IT,您就可以将MyClass<T>分配给MyClass<IT>。您可以有以下类:

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

并做

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

有了这个instance.MyProp List<MyInterfaceImplementation>,但您可以像List<IMyInterface>一样访问它,因此您可以尝试添加在运行时崩溃的MyOtherInterfaceImplementation元素。不好玩。