为什么协变隐式强制转换会忽略泛型约束?

时间:2016-08-22 21:21:42

标签: c# .net

static IEnumerable<U> DoSomething<T, U>(IEnumerable<T> a)
    where T : U
{
    // Works, compiler can compile-time statically cast
    // T as U.
    T testA = default(T);
    U testB = testA;

    // And the following works, though:
    IEnumerable<string> test2A = null;
    IEnumerable<object> test2B = test2A;

    // Doesn’t work. Compiler cannot compile-time statically
    // cast IEnumerable<T> to IEnumerable<U> even though it is
    // out and T is U.
    return a;
}

我有代码可以执行这种类型的隐式转换,这将节省我编写很多样板接口实现代码。 这似乎是协方差应该有助于的那种事情。 但我总是在上面的return a;行上收到此错误:

  

错误CS0266:无法隐式转换类型&#39; System.Collections.Generic.IEnumerable&lt; T&gt;&#39;到&#39; System.Collections.Generic.IEnumerable&lt; U&gt;&#39;。存在显式转换(您是否错过了演员?)

为什么会这样,有没有办法解决这个问题而不做return from o in a select o;之类的事情?

1 个答案:

答案 0 :(得分:6)

当弄乱我的最小版本并阅读类似但无关的问题about interface casting时,我意识到以下编译:

static IEnumerable<U> DoSomethingElse<T, U>(IEnumerable<T> a)
    where T : class, U
{
    // Works! Ridik!
    return a;
}

此外,以下内容失败并显示相同的错误消息:

static void Blah()
{
    // Fails for I bet the same reason that DoSomething{T, U} fails:
    IEnumerable<int> a = null;
    IEnumerable<object> b = a;
}
  

错误CS0266:无法隐式转换类型&#39; System.Collections.Generic.IEnumerable&lt; int&gt;&#39;到&#39; System.Collections.Generic.IEnumerable&lt; object&gt;&#39;。存在显式转换(您是否错过了演员?)

所以这似乎与.net如何将某些类型的赋值限制为引用类型有关,因为在这些情况下装箱会是错误的(例如,您可能会假设引用类型并且实际上正在处理值的副本在运行时很难/不可能实现(给定一个IEnumerable<int>你必须实现一个包装适应类。好的,听起来好像.net不能/不应该尝试做为你在运行时)。我认为它是一种情况,其中.net允许指针式多态,就其本质而言,它与值类型的概念不兼容。

因此,对于我的情况,我不需要在我的API中支持值类型,并添加class约束使一切变得神奇!