【问题标题】:Casting constrained generic class in C#在 C# 中强制转换受约束的泛型类
【发布时间】:2012-12-21 10:00:50
【问题描述】:

很简单,为什么这段代码编译失败?

public interface IWorld { }
public class Foo<T> where T : IWorld { }
public void Hello<T>(T t) where T : IWorld
{
    Foo<IWorld> bar1 = new Foo<T>(); //fails implicit cast
    Foo<IWorld> bar2 = (Foo<IWorld>)new Foo<T>(); //fails explicit cast
}

由于每个T 都实现IWorld,所以Foo&lt;T&gt; 的每个实例都应该匹配Foo&lt;IWorld&gt;。为什么不?有没有办法解决?我真的不想求助于泛型来实现这一点。

【问题讨论】:

  • 值类型可以实现接口。每个值类型都有自己的通用代码实现。 Foo&lt;IWorld&gt; 将使用引用类型实现。所以那里有一个类型不匹配。
  • 即使T 是一个实现接口的值类型,Foo&lt;T&gt; 仍然是一个类,因此是一个引用类型。

标签: c# generics casting


【解决方案1】:
T : IWorld

表示 T 已经实现了 IWorld 并不意味着它只实现了 IWorld 并且完全是 IWorld。它也可能已经实现了其他接口。

但是,C# 在其更高版本中支持这种转换。请参阅http://msdn.microsoft.com/en-us/library/dd799517.aspx(泛型中的协变和逆变)

【讨论】:

  • 是吗?它实际上支持接口和委托的协方差\逆变...
  • 谢谢,我不知道协变和逆变。
【解决方案2】:

您可以先投射到对象

Foo<IWorld> bar2 = (Foo<IWorld>)(object)new Foo<T>();

【讨论】:

    【解决方案3】:

    一个更简单的反对意见 - 假设不是Foo,而是List

    List&lt;T&gt; 转换为List&lt;IWorld&gt; 后,我现在可以将一些其他 IWorld 实现对象(比如T2 类型)添加到限制为仅包含的列表中T 类型的对象。那不应该是有效的。

    回到你的 Foo 对象 - 如果它包含任何期望使用 T 类型的对象调用的方法,我现在可以使用实现 IWorldany 对象调用它们- 即使(想象一下Foo 的附加类型约束)该对象不是Foo 的合格类型。


    我在 cmets re: 值类型中的观点。同样,如果我们根据List&lt;T&gt; 进行讨论,这可能会更容易——值类型的List&lt;T&gt; 包含没有装箱的值类型。如果您想要这些相同值的List&lt;IWorld&gt;,则必须在将每个值添加到列表之前装箱

    【讨论】:

      【解决方案4】:

      跟随有什么问题

       Foo<IWorld> bar1 = new Foo<IWorld>(); 
      

      你想达到什么目的?

      如果您需要传递IWorld 实例,您可以安全地传递T,但您的代码中并非如此。

      编辑(基于 cmets)

      要转换为Foo&lt;Array of something&gt;,您可以使用CastOfType,具体取决于您的要求(是否要抛出或忽略不兼容的匹配项)。

      如果是 .NET 4,由于CoVariance 功能,它应该会自动工作。

      【讨论】:

      • Foo 是一个封装了它所传递的任何泛型数组。我希望Foo 的这个实例包装一个T[] 数组而不是基本IWorld[] 数组。
      猜你喜欢
      • 2017-09-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-02-04
      • 1970-01-01
      相关资源
      最近更新 更多