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

Jam*_*esH 4 .net c# casting interface

我刚刚遇到了我认为类型转换的奇怪之处.我有类似以下代码:

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);
  }
}
Run Code Online (Sandbox Code Playgroud)

当它到达DoSomething(classSet)时,编译器会抱怨它无法将HashSet <MyClass>强制转换为ICollection <IMyClass>.这是为什么?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);
}
Run Code Online (Sandbox Code Playgroud)

对我来说,这个作品的事实使得无法施展更加神秘.

Meh*_*ari 8

它不会工作,因为所有实例MyClass的存在IMyClass并不自动意味着的所有实例HashSet<MyClass>HashSet<IMyClass>.如果有效,你可以:

ICollection<IMyClass> h = new HashSet<MyClass>();
h.Add(new OtherClassThatImplementsIMyClass()); // BOOM!
Run Code Online (Sandbox Code Playgroud)

从技术上讲,它不起作用,因为C#(<= 3.0)泛型是不变的.C#4.0引入了安全协方差,在这种情况下也无济于事.但是,当接口仅在输入中使用类型参数或仅在输出位置使用类型参数时,它确实有帮助.例如,您将能够将a HashSet<MyClass>作为一个IEnumerable<IMyClass>方法传递给某个方法.

顺便说一句,它们比手动填充另一个更容易解决方法HashSet:

var newSet = new HashSet<IMyClass>(originalSet);
Run Code Online (Sandbox Code Playgroud)

或者,Cast如果要将集合强制转换为IEnumerable<IMyClass>:

IEnumerable<IMyClass> sequence = set.Cast<IMyClass>(set);
Run Code Online (Sandbox Code Playgroud)