在Contravariance中,对参考的赋值兼容性进行反转意味着什么?

时间:2018-06-01 14:21:38

标签: c# .net contravariance

在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。由于用户定义的转换不允许进入或来自基类,我看不出方法组的逆转即使工作 - 不会/不应该调用这样的方法抛出{ {1}}以及?

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

2 个答案:

答案 0 :(得分:4)

分配兼容性已反转表示您可以将较少派生的类型分配给更多派生类型。

在示例中,逆向性与Action类一起显示。可以将Action<object>分配给Action<string>

此代码有效:

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

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

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

操作不是协变的,因此无法将Action<string>分配给Action<object>,尝试这样做会引发异常。

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

  • 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>

答案 1 :(得分:0)

你可以查看动作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接口。