我刚刚遇到了我认为类型转换的奇怪之处。我的代码类似于以下内容:
interface IMyClass { }
class MyClass: IMyClass { }
class Main
{
void DoSomething(ICollection<IMyClass> theParameter) { }
HashSet<MyClass> FillMyClassSet()
{
//Do stuff
}
void Main()
{
HashSet<MyClass> classSet = FillMyClassSet();
DoSomething(classSet);
}
}
当它到达DoSomething(classSet)时,编译器会抱怨它无法转换HashSet&lt; MyClass&gt;到ICollection&lt; IMyClass&gt;。这是为什么? HashSet实现了ICollection,MyClass实现了IMyClass,那么为什么不能使用强制转换?
顺便说一下,这并不难解决,认为这有点尴尬。
void Main()
{
HashSet<MyClass> classSet = FillMyClassSet();
HashSet<IMyClass> interfaceSet = new HashSet<IMyClass>();
foreach(IMyClass item in classSet)
{
interfaceSet.Add(item);
}
DoSomething(interfaceSet);
}
对我而言,这种作品使得无法施展更加神秘。
答案 0 :(得分:8)
它不起作用,因为MyClass
IMyClass
的所有实例都不会自动暗示HashSet<MyClass>
的所有实例都是HashSet<IMyClass>
。如果有效,你可以:
ICollection<IMyClass> h = new HashSet<MyClass>();
h.Add(new OtherClassThatImplementsIMyClass()); // BOOM!
从技术上讲,它不起作用,因为C#(&lt; = 3.0)泛型是不变的。 C#4.0引入了安全协方差,在这种情况下也无济于事。但是,当接口仅在输入中使用类型参数或仅在输出位置使用类型参数时,它确实有帮助。例如,您可以将HashSet<MyClass>
作为IEnumerable<IMyClass>
传递给某个方法。
顺便说一句,它们比手动填充另一个HashSet
更容易解决方法,如:
var newSet = new HashSet<IMyClass>(originalSet);
或者,如果您想将一组设置为Cast
,则可以使用IEnumerable<IMyClass>
方法:
IEnumerable<IMyClass> sequence = set.Cast<IMyClass>(set);
答案 1 :(得分:2)
这样的演员实际上并不安全。
考虑以下代码:
class MyOtherClass: IMyClass { }
void DoSomething(ICollection<IMyClass> theParameter) { theParameter.Add(new MyOtherClass(); }
ICollection<MyClass> myClassSet = ...;
DoSomething(classSet); //Oops - myClassSet now has a MyOtherClass
你要求的是协方差;它可用于C#4中的IEnumerable<T>
(只读)。
答案 2 :(得分:0)
它不是一个奇怪的,它是2.0中泛型类型方差的限制。 This blog post describes the situation relatively well.他们在4.0 CLR,IIRC中对此进行了一些改进。