接口方法中与委托的无效差异

len*_*est 1 c# c#-4.0

为什么以下接口声明会产生无效的方差错误?

public interface ICache<in TKey, TValue>
{
  TValue GetOrAdd(TKey key, Func<TKey, TValue> valueFactory);
}
Run Code Online (Sandbox Code Playgroud)

编译器说:

错误 CS1961 无效方差:类型参数“TKey”必须在“ICache<TKey, TValue>.GetOrAdd(TKey, Func<TKey, TValue>)”上协变有效。'TKey' 是逆变的。

问题在于使用TKeyFunc参数。但是TKey根据Func需要被用作输入。为什么它必须是协变的?

Jon*_*eet 5

TL;DR:C# 编译器保证您的安全。

实际上,当涉及到通用方差时,“输入位置中的嵌套输入会产生输出”。您的方法有一个参数,该参数本身接受 a TKey,这会反转方差。

通过想象它是否被允许,最容易理解为什么它被禁止。然后你可以写:

public class ObjectKeyedCache : ICache<object, object>
{
    public object GetOrAdd(object key, Func<object, object> valueFactory)
    {
        // Let's ignore the specified key, and just pass in an object!
        return valueFactory(new object());
    }
}
Run Code Online (Sandbox Code Playgroud)

然后你可以写:

ICache<object, object> objectKeyedCache = new ObjectKeyedCache();
// Ah, due to contravariance, this should be okay...
ICache<string, object> stringKeyedCache = objectKeyedCache;

// Okay, this is a weird cache function, but bear with me
stringKeyedCache("key", text => text.Length);
Run Code Online (Sandbox Code Playgroud)

然后会尝试将object引用传递到Func<string, object>从 lambda 表达式创建的对象中text => text.Length。砰!

有关更多详细信息,请阅读Eric Lippert 的方差博客系列的第 5 部分。这是该帖子最重要的部分:

这样做会让我的大脑受伤,但这也能培养性格,所以我们开始吧!

任何让埃里克脑部受伤的事情都应该被视为对其余部分的主要健康危害。你被警告了。

  • 这就是我们将“in”和“out”作为表示差异的关键字的原因之一。有时“进入”的事物在逆变上下文中实际上无效这一事实令人困惑。但是其他选择,比如像某些语言那样使用“+”和“-”,似乎更加混乱。 (3认同)