C#逆变和协变的理解-----为什么逆变可以把Object类型的类型参数转换成String类型的类型参数

2021-05-06 15:26

阅读:643

标签:line   安全   接口   namespace   show   基类   误区   强制转换   指定   

根据可变性的规则,只有接口和委托可以标记可变性。且只有类型参数为引用类型时才可以利用可变性。

不变性:泛型类型的参数不能改变,这表示一个创建一个MyInterface类型的对象时,赋值给它的只能是MyInterface类型

 1 namespace ContravariantAndCovariant
 2 {
 3     public interface MyInterface//没有指定任何可变性,说明这个接口的类型参数是不变的
 4     {
 5         void Show(T t);
 6     }
 7 
 8     public class ShowObject : MyInterface
 9     {
10         public void Show(object t)
11         {
12             Console.WriteLine(t.ToString());
13         }
14     }
15 
16     public class ShowString : MyInterface17     {
18         public void Show(string t)
19         {
20             Console.WriteLine(t);
21         }
22     }
23 
24     public class ShowInt : MyInterface25     {
26         public void Show(int t)
27         {
28             Console.WriteLine(t.ToString());
29         }
30     }
31 
32     class Program
33     {
34         static void Main(string[] args)
35         {
36             //正确
37             MyInterface str_invariant = new ShowString();//只能赋值MyInterface类型
38             //错误
39             MyInterface str_contravariant = new ShowObject();//当赋值其它类型的时候,编译器会报错
40             //CS0266    无法将类型“ContravariantAndCovariant.ShowObject”隐式转换为“ContravariantAndCovariant.MyInterface”。存在一个显式转换(是否缺少强制转换?)    
41 
42         }
43     }
44 
45 }

逆变性:泛型的类型参数可以从一个类变成它的派生类,比如:可以把MyInterface转换成MyInterface。逆变用in表示,因为它出现在输入的位置(方法的参数),in 就是进去的意思。

 1 namespace ContravariantAndCovariant
 2 {
 3     public interface MyInterfacein T>//用in关键字指定T的逆变性
 4     {
 5         void Show(T t);//T是参数类型
 6     }
 7 
 8     public class ShowObject : MyInterface
 9     {
10         public void Show(object t)
11         {
12             Console.WriteLine(t.ToString());
13         }
14     }
15 
16     public class ShowString : MyInterface17     {
18         public void Show(string t)
19         {
20             Console.WriteLine(t);
21         }
22     }
23 
24     public class ShowInt : MyInterface25     {
26         public void Show(int t)
27         {
28             Console.WriteLine(t.ToString());
29         }
30     }
31 
32     class Program
33     {
34         static void Main(string[] args)
35         {
36             MyInterfaceobject> obj = null;
37             MyInterfacestring> str = obj;//可以把MyInterface类型转换为MyInterface类型
38         }
39     }
40 
41 }

协变性:泛型的类型参数可以从一个类变成它的基类,比如:可以把MyInterface转换成MyInterface。逆变用out表示,因为它出现在输出的位置(方法返回值),out 就是出的意思。

 1 namespace ContravariantAndCovariant
 2 {
 3     public interface MyInterfaceout T>//用out关键字指定T的协变性
 4     {
 5         T Show(); //T是返回类型
 6     }
 7 
 8     public class ShowObject : MyInterface
 9     {
10         public object Show()
11         {
12             return null;
13         }
14     }
15 
16     public class ShowString : MyInterface17     {
18         public string Show()
19         {
20             return null;
21         }
22     }
23 
24     class Program
25     {
26         static void Main(string[] args)
27         {
28             //错误
29             //MyInterface obj = null;
30             //MyInterface str = obj;//不能把MyInterface类型转换为MyInterface类型
31 
32             //正确
33             MyInterfacestring> str = null;
34             MyInterfaceobject> obj = str;//可以把MyInterface类型转换成MyInterface类型
35         }
36     }
37 }

CLR是要保证类型的转换是安全的,协变性还好理解,子类是可以转换成父类的,因为子类对象中包括父类对象的所有成员。这个转换是安全的。

但是逆变的话,父类转换成子类是不安全的,当不能里氏转换的时候,父类对象中并没有子类的所有成员。

其实这么看的话是有一个误区,就是关于引用和对象。当逆变的时候,比如上面逆变的代码中:

MyInterface obj = null;

MyInterface str = obj;

str.Show("123")

str是MyInterface类型的变量,但是它引用的对象是MyInterface类型的,所以当我们调用Show()方法的时候,其实是obj.Show();

obj的参数是object类型的,我们传入的是一个string类型,string---->object的转换是安全的。所以逆变是安全的!

同理的话,协变:

MyInterface str = null;

Myinterface obj = str;

 object  result = obj.show();

可以看到obj是MyInterface类型的变量,但是它引用的对象是MyInterface类型,所以当我们调用Show()方法的时候,其实是str.Show(),

它返回的是string类型,但是obj.Show()返回的是一个object类型,所以我们用一个object类型的变量result去接收返回值,最终变成 string ----->object类型的转换,所以协变是安全的。

C#逆变和协变的理解-----为什么逆变可以把Object类型的类型参数转换成String类型的类型参数

标签:line   安全   接口   namespace   show   基类   误区   强制转换   指定   

原文地址:http://www.cnblogs.com/marsir/p/7658822.html


评论


亲,登录后才可以留言!