C#Type-casting oddity - 接口作为泛型类型

时间:2010-01-20 20:42:32

标签: c# .net interface casting

我刚刚遇到了我认为类型转换的奇怪之处。我的代码类似于以下内容:

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

对我而言,这种作品使得无法施展更加神秘。

3 个答案:

答案 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中对此进行了一些改进。