在逆变中,反转引用的赋值兼容性意味着什么?

In Contravariance, what does it mean for a reference's assignment compatibility to be reversed?

在逆变中,引用的赋值兼容性被反转是什么意思

Covariance and Contravariance (C#)

// Assignment compatibility.   
string str = "test";  
// An object of a more derived type is assigned to an object of a less derived type.   
object obj = str;  

// Covariance.   
IEnumerable<string> strings = new List<string>();  
// An object that is instantiated with a more derived type argument   
// is assigned to an object instantiated with a less derived type argument.   
// Assignment compatibility is preserved.   
IEnumerable<object> objects = strings;  

// Contravariance.             
// Assume that the following method is in the class:   
// static void SetObject(object o) { }   
Action<object> actObject = SetObject;  
// An object that is instantiated with a less derived type argument   
// is assigned to an object instantiated with a more derived type argument.   
// Assignment compatibility is reversed.   
Action<string> actString = actObject;

参考: https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/covariance-contravariance/

此外,当尝试从派生较少的类型转换为派生较多的类型时,会抛出 InvalidCastException。由于 不允许用户定义的转换进出基 class,我看不出方法组的逆变如何工作 - 不会't/shouldn调用这样的方法也不会抛出 InvalidCastException 吗?


更新:https://blogs.msdn.microsoft.com/ericlippert/2009/11/30/whats-the-difference-between-covariance-and-assignment-compatibility/

赋值兼容性是相反的意味着您可以将派生程度较低的类型分配给派生程度较高的类型。

在示例中,逆变显示为 Action class。 Action<object> 可以分配给 Action<string>

此代码有效:

public void Test(object o)
{
    Console.WriteLine(o.GetType().ToString());
}

Action<string> foo;
foo = Test;
foo.Invoke("bar");

在这个例子中,我们可以看到字符串 "bar" 在作为参数传递给 Test 方法时被隐式转换为对象类型.没有涉及无效的转换或用户定义的转换。

Action 不是协变的,所以 Action<string> 不能赋值给 Action<object>,这样做会抛出异常。

当泛型类型是输入参数并且与输出参数协变时,泛型类型是逆变的。 所以 Func class 与它的第一种类型是协变的,并且与它的第二种类型(和第三种,...)是逆变的:

  • Func<string, object> 可以分配给 Func<string, string>
  • Func<string, string> 可以分配给 Func<object, string>
  • Func<string, object> 可以分配给 Func<object, string>
  • Func<object, string> 无法分配给 Func<string, string>

要在 C# 中声明逆变泛型类型,我们可以使用 in 关键字声明逆变接口。在这样做时,我们被限制使用泛型类型作为输入参数:

interface IMyGenericType<in T>
{
    object Test(T input); //allowed
    //T GetValue(); //not allowed in a contravariant interface
}

使用这种类型,我可以将 IMyGenericType<object> 分配给 IMyGenericType<string>

您可以查看 Action https://msdn.microsoft.com/en-us/library/018hxwa8(v=vs.110).aspx 参考资料,其中说 T 参数是通过 in 传递的,这意味着它是逆变的。

 static object GetObject() { return null; }  
 static void SetObject(object obj) { }  

static string GetString() { return ""; }  
static void SetString(string str) { }  

static void Test()  
{  
    // Covariance. A delegate specifies a return type as object,  
    // but you can assign a method that returns a string.  
    Func<object> del = GetString;  

    // Contravariance. A delegate specifies a parameter type as string,  
    // but you can assign a method that takes an object.  
    Action<string> del2 = SetObject;  
}  

在这个例子中,逆变很明显并且有效,因为你将传递给 del2 的任何字符串都可以变成 object.So 逆变应该在实现逆变解决方案合乎逻辑的地方实现,比如 Action委托类型参数,另一个例子是 IComparer 接口。