C# 类型转换怪癖 - 接口(interface)作为泛型类型

标签 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 转换为 ICollection。这是为什么? 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);
}

对我来说,这个有效的事实让无法转换变得更加神秘。

最佳答案

它不会工作,因为 MyClass 的所有实例正在IMyClass不会自动暗示 HashSet<MyClass> 的所有实例也是HashSet<IMyClass> .如果有效,您可以:

ICollection<IMyClass> h = new HashSet<MyClass>();
h.Add(new OtherClassThatImplementsIMyClass()); // BOOM!

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

顺便说一下,它们比手动填充另一个 HashSet 更容易。喜欢:

var newSet = new HashSet<IMyClass>(originalSet);

或者您可以使用 Cast方法,如果你想将一个集合转换为 IEnumerable<IMyClass> :

IEnumerable<IMyClass> sequence = set.Cast<IMyClass>(set);

关于C# 类型转换怪癖 - 接口(interface)作为泛型类型,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2104802/

相关文章:

c# - iphone 推送通知 urbanairship

c# - 异步 TcpClient 没有及时读取所有数据

c# - 监控 .net 中的互联网使用情况

.net - dotnet发布包含额外的文件

C# 4.0 动态对象和 WinAPI 接口(interface),如 IShellItem(无需在 C# 源代码中定义它们)

c# - 关闭由 ShowDialog() 打开的表单

c# - Silverlight:如何使用 Webclient 异步模式将数据从请求传递到响应?

c# - 将 DataRow 项设置为 null

c# - 修复继承困惑的接口(interface)?

interface - 依赖注入(inject) - 与数据传输对象 (DTO) 一起使用?