【问题标题】:Why can't I use output parameters with covariant generic types?为什么我不能将输出参数与协变泛型类型一起使用?
【发布时间】:2014-07-03 04:22:32
【问题描述】:

我尝试了以下,结果在接口名称中:

interface NotOK<out T>
{
    bool TryDequeue(out T t);
}

interface OK<out T>
{
    T TryDequeue(out bool b);
}

The docs有话要说:

C# 中的

refout 参数不能变体。

为什么 ref 不能是协变的(或逆变的,就此而言)是显而易见的,但为什么 out 参数不能是协变的,就像方法结果一样?

这是编译器限制还是 out 参数 实际上会破坏协方差约束?

【问题讨论】:

标签: .net generics covariance output-parameter


【解决方案1】:

我的问题实际上已经在ref and out parameters in C# and cannot be marked as variant得到了答案

Eric Lippert's great answer 的相关部分(但还有更多):

很遗憾,没有。 “out”实际上与幕后的“ref”没有什么不同。 “out”和“ref”的唯一区别是编译器禁止在被调用者赋值之前读取out参数,并且编译器要求在被调用者正常返回之前进行赋值。使用 C# 以外的 .NET 语言编写此接口的实现的人将能够在项目初始化之前读取它,因此它可以用作输入。因此,在这种情况下,我们禁止将 T 标记为“out”。这很遗憾,但我们无能为力;我们必须遵守 CLR 的类型安全规则。

【讨论】:

    【解决方案2】:

    这是因为如果您使用 'out' 参数修饰符进行协方差,编译器可能会认为需要进行不安全的类型转换。

    看到这个场景。假设有一个方法 f 期望 NotOK 作为输入:

    interface NotOK<out T>
    {
        bool TryDequeue(out T t);
    }
    
    void f( NotOK<Animal> x)
    {
       bool b ;
       Animal animal_in_f;
       b = x.TryDequeue(animal_in_f);
    }
    

    看看如果我有两个接口会发生什么:

    NotOK<Animal> objA;
    NotOK<Dog> objD;
    

    使用objA作为f的输入,没问题。

    f(objA);
    // objA should have a method of signature bool TryDequeue(out Animal t)
    // inside method f, it calls x.TryDequeue(animal_in_f); 
    // where animal_in_f is Animal, type match
    

    但如果允许协方差,则允许传递 objD

    f(objD);
    // objD should have a method of signature bool TryDequeue(out Dog t)
    // inside method f, it calls x.TryDequeue(animal_in_f); 
    // where animal_in_f is Animal
    // but this time x.TryDequeue is expecting a Dog!!
    // It is unsafe to cast animal_in_f to Dog
    

    所以你明白为什么不允许在协方差中使用 out 了。

    我认为从概念上讲它应该可以工作,因为通过使用 out 参数修饰符,我们只希望传递的变量作为输出。如果编译器有一个特殊的规则,它就会起作用,这样当它遇到上面的场景时,它应该认为强制转换是安全的,不会产生错误。

    但我认为 C# 设计者权衡了利弊,最终决定保持一致的类型检查规则,这通常是不允许向下转换的。

    在我看来,最好添加这个特殊规则,因为现在它限制了使用,说它不能有一个方法返回两个需要使用 out 参数修饰符的 T 类型对象。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-10-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-02-10
      • 1970-01-01
      • 1970-01-01
      • 2016-05-14
      相关资源
      最近更新 更多