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


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

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

     1 namespace ContravariantAndCovariant
     2 {
     3     public interface MyInterface<T>//没有指定任何可变性,说明这个接口的类型参数是不变的
     4     {
     5         void Show(T t);
     6     }
     7 
     8     public class ShowObject : MyInterface<Object>
     9     {
    10         public void Show(object t)
    11         {
    12             Console.WriteLine(t.ToString());
    13         }
    14     }
    15 
    16     public class ShowString : MyInterface<String>
    17     {
    18         public void Show(string t)
    19         {
    20             Console.WriteLine(t);
    21         }
    22     }
    23 
    24     public class ShowInt : MyInterface<Int32>
    25     {
    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<String> str_invariant = new ShowString();//只能赋值MyInterface<String>类型
    38             //错误
    39             MyInterface<String> str_contravariant = new ShowObject();//当赋值其它类型的时候,编译器会报错
    40             //CS0266    无法将类型“ContravariantAndCovariant.ShowObject”隐式转换为“ContravariantAndCovariant.MyInterface<string>”。存在一个显式转换(是否缺少强制转换?)    
    41 
    42         }
    43     }
    44 
    45 }

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

     1 namespace ContravariantAndCovariant
     2 {
     3     public interface MyInterface<in T>//用in关键字指定T的逆变性
     4     {
     5         void Show(T t);//T是参数类型
     6     }
     7 
     8     public class ShowObject : MyInterface<Object>
     9     {
    10         public void Show(object t)
    11         {
    12             Console.WriteLine(t.ToString());
    13         }
    14     }
    15 
    16     public class ShowString : MyInterface<String>
    17     {
    18         public void Show(string t)
    19         {
    20             Console.WriteLine(t);
    21         }
    22     }
    23 
    24     public class ShowInt : MyInterface<Int32>
    25     {
    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             MyInterface<object> obj = null;
    37             MyInterface<string> str = obj;//可以把MyInterface<object>类型转换为MyInterface<string>类型
    38         }
    39     }
    40 
    41 }

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

     1 namespace ContravariantAndCovariant
     2 {
     3     public interface MyInterface<out T>//用out关键字指定T的协变性
     4     {
     5         T Show(); //T是返回类型
     6     }
     7 
     8     public class ShowObject : MyInterface<Object>
     9     {
    10         public object Show()
    11         {
    12             return null;
    13         }
    14     }
    15 
    16     public class ShowString : MyInterface<String>
    17     {
    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<object> obj = null;
    30             //MyInterface<string> str = obj;//不能把MyInterface<object>类型转换为MyInterface<string>类型
    31 
    32             //正确
    33             MyInterface<string> str = null;
    34             MyInterface<object> obj = str;//可以把MyInterface<string>类型转换成MyInterface<Object>类型
    35         }
    36     }
    37 }

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

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

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

    MyInterface<object> obj = null;

    MyInterface<string> str = obj;

    str.Show("123")

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

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

    同理的话,协变:

    MyInterface<string> str = null;

    Myinterface<object> obj = str;

     object  result = obj.show();

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

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

  • 相关阅读:
    对获取的DataTable表进行过滤筛选得到DataView
    简单提取iOS13的ipsw固件的内置壁纸(或文件)
    win10设置Python程序定时运行(设置计划任务)
    后端返回一个这种类型的时间格式给前端2020-01-16T09:10:02.349Z
    js把每个词的首字母转大写
    idea连接mysql自动生成实体类
    el自定义函数
    js日期时间格式化
    js大小写转换
    js瞄点
  • 原文地址:https://www.cnblogs.com/marsir/p/7658822.html
Copyright © 2020-2023  润新知