C# 不理解委托的协变性和逆变性

4

长期以来,我一直试图理解在C#泛型中使用"in"和"out"参数的用途,但我就是无法理解(我知道这个问题在StackOverflow上经常被问到)。我通常理解什么是协变性和逆变性,只是不明白为什么需要使用"in"和"out"参数。

以下是示例:

public class MainClass {

delegate TOut MyDelegate<TIn, TOut>(TIn input);

public static void Main()
{
    // Func Delegate is using "in T, out TResult"
    Func<Dog, Mammal> funcDelegate = TestMethod;

    // not using "in" or "out" parameters
    MyDelegate<Dog, Mammal> myDelegate = TestMethod;
}

static Dog TestMethod(Mammal m) { return new Dog(); }

class Mammal { }
class Dog : Mammal { } }//end of class

为什么 Func 委托在我的委托没有“in”和“out”的情况下也可以引用协变和逆变的方法时,还要使用“in”和“out”?


"TIn in" 是无效的声明,这里 "in" 是不正确的标识符。 - Pavel Anikhouski
@GuruStron 抱歉,“in” 实际上应该是“输入”。这是从 Visual Studio 复制时的错误。我已经更正了。 - Coco07
@PavelAnikhouski 如果没有项目<3.5,那么今天您可以从所有委托定义中删除“in”和“out”吗? - Coco07
@PavelAnikhouski 那很有道理。我在谷歌上搜索了200个类似的条目,但从未得到微软提供的这些信息。也许我应该参加一门谷歌课程,非常感谢你们两位! :) - Coco07
@Coco07 我已经添加了一个答案,概括了上面的评论,并附有一些简要解释。 - Pavel Anikhouski
显示剩余3条评论
2个回答

3
根据问题下的评论,委托变异文档将会解释:在C#中,.NET Framework 3.5引入了对所有委托类型匹配方法签名的协变性支持。这意味着您不仅可以将具有匹配签名的方法分配给委托,而且还可以将返回更派生类型(协变)或接受比委托类型指定的参数更少派生类型(逆变)的方法分配给委托。
因此,您的分配MyDelegate<Dog, Mammal> myDelegate = TestMethod;是完全可以的,尽管委托和TestMethod的签名不同(输入参数和返回类型颠倒)。
但是泛型类型参数的变异部分指出,当委托之间存在隐式转换时,仍然需要inout参数。
为了启用隐式转换,必须使用inout关键字在委托中显式声明泛型参数的协变或逆变。
例如,以下代码将无法编译:
MyDelegate<Dog, Mammal> myDelegate = TestMethod;
MyDelegate<Dog, Dog> anotherDelegate = TestMethod;
myDelegate = anotherDelegate; //error CS0029: Cannot implicitly convert type...

除非你使用逆变参数和协变返回类型声明 MyDelegate

delegate TOut MyDelegate<in TIn, out TOut>(TIn input);

在那之后,最后一行将会被编译。


2
除了@Pavel Anikhouski的评论(链接到this文章),当你尝试做下一步时,方差也会发挥作用,例如:
Func<Mammal, Mammal> someFunc = TestMethod;
Func<Dog, Mammal> funcDelegate = someFunc;
MyDelegate<Mammal, Mammal> someDelegate = TestMethod;
MyDelegate<Dog, Mammal> myDelegate = someDelegate; // will not compile unless you will declare MyDelegate<in TIn, TOut>

2
非常感谢!我现在觉得我已经理解了,或者至少可以继续朝正确的方向阅读 :) - Coco07
老实说,这个“答案”没有提供任何新的观点,已经在所提到的文章和问题下的评论中阐述过了。它似乎只是为了刷声望分数而已。 - Pavel Anikhouski
如果 @PavelAnikhouski 的评论被写成了答案,我会将其标记为已接受的 - 我仍然很高兴。 - Coco07
@PavelAnikhouski 在看到你的评论之前我已经开始写了,而且说实话我没有读完第一条评论中你添加的引用之后的文章。同时我认为你的评论很好,可以作为答案添加进去。 - Guru Stron

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接