将结构作为通用协变参数

时间:2015-01-22 18:10:11

标签: c# generics struct covariance

我有一个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
};

那为什么这不起作用?有没有办法让它发挥作用?

3 个答案:

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

根据您的其他要求,这可能是也可能不够。