gpt4 book ai didi

c# - 泛型中 Func 的协变和逆变

转载 作者:太空狗 更新时间:2023-10-29 17:41:17 24 4
gpt4 key购买 nike

我需要有关泛型和委托(delegate)方差的更多信息。以下代码片段无法编译:

Error CS1961 Invalid variance: The type parameter 'TIn' must be covariantly valid on 'Test.F(Func)'. 'TIn' is contravariant.

public interface Test<in TIn, out TOut>
{
TOut F (Func<TIn, TOut> transform);
}

.net Func定义如下:

public delegate TResult Func<in T, out TResult> (T arg);

为什么编译器提示 TIn 是逆变的,而 TOut 是协变的,而 Func 期望完全相同的方差?

编辑

对我来说主要的限制是我希望我的测试接口(interface)有 TOut 作为协变,以便像这样使用它:

public Test<SomeClass, ISomeInterface> GetSomething ()
{
return new TestClass<SomeClass, AnotherClass> ();
}

鉴于 public class AnotherClass : ISomeInterface

最佳答案

I need more information about variance in generics and delegates.

我写了一系列关于此功能的博客文章。尽管其中一些已经过时——因为它是在设计定稿之前编写的——但那里有很多有用的信息。特别是如果您需要方差有效性的正式定义,您应该仔细阅读:

https://blogs.msdn.microsoft.com/ericlippert/2009/12/03/exact-rules-for-variance-validity/

有关相关主题,请参阅我在 MSDN 和 WordPress 博客上的其他文章。


Why the compiler complains about TIn being contravariant and TOut - covariant while the Func expects exactly the same variance?

让我们稍微重写一下代码看看:

public delegate R F<in T, out R> (T arg);
public interface I<in A, out B>{
B M(F<A, B> f);
}

编译器必须证明这是安全,但事实并非如此。

我们可以通过假设它是安全的来说明它是不安全的,然后发现它是如何被滥用的。

假设我们有一个具有明显关系的动物层次结构,例如,哺乳动物是一种动物,长颈鹿是一种哺乳动物,等等。假设您的差异注释是合法的。我们应该能够说:

class C : I<Mammal, Mammal>
{
public Mammal M(F<Mammal, Mammal> f) {
return f(new Giraffe());
}
}

我希望您同意这是一个完全有效的实现。现在我们可以这样做:

I<Tiger, Animal> i = new C();

C工具 I<Mammal, Mammal> ,我们说过第一个可以变得更具体,第二个可以变得更一般,所以我们做到了。

现在我们可以这样做:

Func<Tiger, Animal> f = (Tiger t) => new Lizard();

对于这个代表来说,这是一个完全合法的 lambda,它与以下人的签名相匹配:

i.M(f);

然后会发生什么? C.M期待一个接受长颈鹿并返回哺乳动物的函数,但它被赋予了一个接受老虎并返回蜥蜴的函数,所以有人会有非常糟糕的一天。

显然,这种情况是不允许发生的,但是沿途的每一步都是合法的。我们必须得出结论,方差本身并不能证明是安全的,事实上,事实并非如此。编译器拒绝这一点是正确的。

获得正确的差异不仅仅是简单地匹配输入和输出注释。 您必须以不允许此类缺陷存在的方式这样做。

这解释了为什么这是非法的。为了解释如何它是非法的,编译器必须检查B M(F<A, B> f); 是否符合以下条件。 :

  • B协变有效的。既然它被宣布为“out”,它就是。
  • F<A, B> 逆变有效。它不是。泛型委托(delegate)的“逆变有效”定义的相关部分是:如果第 i 个类型参数被声明为逆变,则 Ti 必须是协变有效的。 好的。第一个类型参数T , 被宣布为逆变的。因此第一个类型参数 A必须协变有效。但它不是协变有效的,因为它被声明为逆变的。这就是你得到的错误。同样,B也很糟糕,因为它必须逆变地有效,但是 B是协变的。编译器在这里发现第一个问题后不会继续寻找其他错误;我考虑过它,但拒绝了它,因为它是一条过于复杂的错误消息。

我还注意到,即使委托(delegate)不是变体,您仍然会遇到这个问题;在我的反例中,我们没有在任何地方使用 F 在其类型参数中是可变的这一事实。如果我们尝试会报类似的错误

public delegate R F<T, R> (T arg);

相反。

关于c# - 泛型中 Func 的协变和逆变,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48177452/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com