我有一个Covariant接口,它是父类型的数组。 它们在全球范围内定义如下:
//The interface/class
public interface IMyType<out T>{}
public class MyType<T>: IMyType<T>{
T value;
MyType<T>(T initialValue){
value = initialValue;
}
}
我试过了:
IMyType< object>[] tt = new IMyType<object>[]
{
new MyType<String>( "test"), //Works fine
new MyType<SomeOtherRandomClass>(new SomeOtherRandomClass()),//works fine
new MyType<Int32>(12)//Doesn't work
};
甚至尝试指定结构而不是对象:
IMyType< struct>[] tt = new IMyType<struct>[]//Doesn't work:Incorrect Number of parameter
{
new MyType<Int32>(12)//Doesn't work
};
仅指定特定结构:
IMyType< int>[] tt = new IMyType<int>[]//Does work
{
new MyType<Int32>(12)//Does work
};
那为什么这不起作用?有没有办法让它发挥作用?
答案 0 :(得分:4)
完全无法将Interface<ValueType>
转换为Interface<object>
作为协变转换。
原因是JITter必须为每个值类型编译一个单独的类版本(因为值类型具有不同的形状),所以它不能将它转换为任何其他通用实例。
方差仅适用于引用类型,因为引用完全相同,因此可以跨不同的类型参数共享JITted方法。
答案 1 :(得分:1)
您最好的解决方案可能是编写一个封装结构的包装类:
public class StructContainer<TStruct> where TStruct : struct
{
TStruct value;
public StructContainer(TStruct initialValue)
{
value = initialValue;
}
}
然后你可以这样做:
IMyType<object>[] tt = new IMyType<object>[]
{
new MyType<String>("test"), //Works fine
new MyType<StructContainer<int>>(
new StructContainer<int>(12)
) // compiles now
};
当然,这会牺牲原始语法的简单性,并迫使您在以后需要从收集中提取项目时执行类似检查(<类型检查)。
另一个选项是跟踪对象集合和结构集合作为单独的集合。这可能更有意义,因为再次,一旦您将项目从列表中拉出,您可能需要以不同的方式对待它们。
答案 2 :(得分:0)
如果你想完成将具有不同类型参数的泛型类型放入同一个集合中,并且由于SLaks提到的原因你不能使用协方差,那么你应该让泛型类型实现非泛型接口:< / p>
public interface IMyType<out T> : ISomeBaseInterface {}
var tt = new ISomeBaseInterface[]
{
new MyType<String>( "test"), //Works fine
new MyType<SomeOtherRandomClass>(new SomeOtherRandomClass()),//works fine
new MyType<Int32>(12)//now it works
};
根据您的其他要求,这可能是也可能不够。