<T> 从协方差返回操作的方法



它具有以下interface:

public interface ICovariant<out T>
{
public void InputFunc(Func<T> func);
}

我得到以下编译时错误:

无效方差:协变类型参数'T'用于逆变位置。参数必须输入安全

Func<T>的位置是输入Func<T>Func<out T>,所以它是输出

好吧,我错了。获取与位置相关的信息。

但是,当我添加以下方法时,会出现一个错误。

public interface ICovariant<out T>
{
public void InputFunc(Func<T> func);
public Action<T> ReturnAction();
}

无效方差:协变类型参数'T'用于逆变位置。方法返回类型必须是输出安全

Action<T>的位置是输出Action<T>Action<in T>,所以它是输入

什么?与以前不同的是,这次地点无关紧要。

为什么这两个例子对彼此的看法不同?

清扫者接受的答案是好的;我想提供一种稍微不同的方式来思考为什么你提出的计划是非法的。在此之前,请允许我再次为这里错误消息的低质量道歉。我想不出更好的办法了,显然在接下来的15年里,它们没有得到太大的改善。

public interface ICovariant<out T>
{
public Action<T> ReturnAction(); // Why is this illegal?
}

为什么这是违法的?试着去实现它,然后看看哪里出了问题!

class Animal {...}
class Mammal : Animal {...}
// ... and so on ...
class Danger: ICovariant<Mammal>
{
public Action<Mammal> ReturnAction()
{
return (Mammal m) => { m.ProduceMilk(); };
}
}

Danger满足ICovariant<Mammal>的要求——它返回一个动作,这个动作带走一只哺乳动物,它做一件事——让哺乳动物产奶:

ICovariant<Mammal> danger = new Danger();
Action<Mammal> action = danger.ReturnAction();
action(new Goat()); // Goat milk, mmmmm

但是ICovariant<Mammal>可以转换为ICovariant<Animal>,因为它是协变的,所以我们也可以写:

ICovariant<Animal> danger = new Danger();
// ICov<Animal> produces an Action<Animal>
Action<Animal> action = danger.ReturnAction();
action(new Chicken());

现在我们挤了一只鸡的奶。这种尝试不会有什么好处。

上面的线条完全合法;它们不可能是错误。为了防止这种情况发生,编译器必须使接口本身非法。

与以前不同,这次位置无关紧要。

位置确实很重要!这编译得很完美:

public interface ICovariant<out T>
{
public void AcceptAction(Action<T> action);
}

Action中的逆变类型参数的作用是,它使其对应的类型参数具有与您放置它的位置相反的位置-如果它最初位于输入位置,则它现在位于输出位置,如果它最初处于输出位置,则现在处于输入位置。

"Contra-";是指";相反";,所以这是一个很容易记住的行为:D尽管这个特定的行为可能不是名字"的地方;反向方差";来自。Eric Lippert在这里的回答解释了为什么这么叫。

所以这也编译了(T的位置被"翻转"了两次,所以它回到了输出位置(:

public Action<Action<T>> ReturnsNestedAction();

如果你只是想一想你实际上是如何调用ReturnAction的,你应该能够直观地说服自己,事实上,ReturnAction中的T在一个输入位置:

Action<T> action = someICovariant.ReturnAction();
action(someT); // I am *inputting* an instance of T here!

同样,您可以对AcceptAction执行同样的操作,并看到T确实处于输出位置:

someICovariant.AcceptAction(t => {
Console.WriteLine(t);
});
/*
Imagine the implementation of AcceptAction invoking its parameter at some point.
That is analogous to AcceptAction *returning* some instance of T and we print it out
The difference being that instead of "return someT;", 
AcceptAction would do "action(someT);", but the fact it is producing an instance of T
doesn't change
*/

如果您对如何在技术术语中指定这一点感兴趣,可以阅读语言规范的这一部分。

还参见关于类似的";翻转位置";声明逆变类型参数时的行为。

相关内容

  • 没有找到相关文章

最新更新