zoukankan      html  css  js  c++  java
  • CSharpThinking协变和逆变(一)

    实践是检验真理的捷径。

      本章主要是理解C#中的协变和逆变的关系,对今后理解泛型会有很大帮助。

    1.协变与逆变的概念及代码说明。

     C#1:数组是强类型,强类型不允许内部数据不能相互转换。C#2中引入了对协变与逆变的限制,而C#4中又适当放宽了政策,不过这一切对数组没有任何影响,不过可以用一系列的接口和集合代替数组。

     1             Stephen[] MySelf = new Stephen[1];
     2             MySelf[0] = new Stephen() { Name = "name1", Name2 = "name2" };
     3             StephenBase[] MyBase = MySelf; // 协变
     4             Stephen[] OtherMe = (Stephen[])MyBase; // 逆变
     5 
     6 ...
     7 
     8         public class StephenBase
     9         {
    10             public StephenBase() { }
    11             public string Name { get; set; }
    12         }
    13 
    14         public class Stephen : StephenBase
    15         {
    16             public Stephen()
    17             {
    18 
    19             }
    20 
    21             public string Name2 { get; set; }
    22         }

    委托的协变和逆变

    逆变:其中void KeyPressEventHandler(object sender, KeyPressEventArgs e) 继承自 void EventHandler(object sender, EventArgs e)

    1 void LogPlainEvent(object sender ,EventArgs e)
    2 {
    3      Console.WriteLine("An event occurred!");
    4 }
    5 
    6 Button btn = new Button();
    7 btn.Click += LogPlainEvent; // 委托逆变
    8 btn.KeyPress += LogPlainEvent; // 委托逆变

    协变:

     1            delegate Stream StreamFactory();
     2 
     3             StreamFactory sf = GenerateMeoryStream; // 委托协变
     4             using (Stream s = sf())
     5             {
     6                 int data;
     7                 while ((data = s.ReadByte()) != -1)
     8                 {
     9                     Console.WriteLine(data);
    10                 }
    11             } 
    12 
    13         static MemoryStream GenerateMeoryStream()
    14         {
    15             byte[] buffer = new byte[16];
    16             for (int i = 0; i < buffer.Length; i++)
    17             {
    18                 buffer[i] = (byte)i;
    19             }
    20             return new MemoryStream(buffer);
    21         }

    注:协变和逆变是指从基类转换成子类或从子类转换到基类。

    2.协变与逆变转换时,本质上没有改变对象,只是编译器如何看待这些值

    1             Stephen[] MySelf = new Stephen[1];
    2             MySelf[0] = new Stephen() { Name = "name1", Name2 = "name2" }; 
    3             StephenBase[] MyBase = MySelf; // 协变
    4             Stephen[] OtherMe = (Stephen[])MyBase; // 逆变
    5 
    6             Console.WriteLine("ChildName: " + OtherMe[0].Name2);
    7             Console.ReadLine();

     接口继承也会在二次复原转换后依然保留原始信息。

     1         public static void InterfaceTest()
     2         {
     3             HelloStephen stephen = new HelloStephen()
     4             {
     5                 Email = @"Balabala@happy.com",
     6                 Oicq = @"10001",
     7                 IsAlive = true
     8             };
     9 
    10             IHelloStephen istephen = (IHelloStephen)stephen;
    11             HelloStephen targetStephen = HelloStephen.FindStephen(istephen);
    12 
    13             Console.WriteLine("Is Stephen alive ?");
    14             Console.WriteLine(targetStephen.IsAlive);
    15             Console.WriteLine("Contact Me ? To : " + targetStephen.Email + " Or QQ : " + targetStephen.Oicq);
    16         }
     1     /// <summary>
     2     /// Descrption:
     3     /// IStephen Interface
     4     /// </summary>
     5     public interface IHelloStephen
     6     {
     7         string Email { get; set; }
     8         string Oicq { get; set; }
     9         bool IsAlive { get; set; }
    10     }
    11 
    12     public class HelloStephen : IHelloStephen
    13     {
    14         #region IHelloStephen
    15         string _Email;
    16         string _Oicq;
    17         bool _isAlive;
    18 
    19         public bool IsAlive
    20         {
    21             get { return _isAlive; }
    22             set { _isAlive = value; }
    23         }
    24         public string Email
    25         {
    26             get { return _Email; }
    27             set { _Email = value; }
    28         }
    29         public string Oicq
    30         {
    31             get { return _Oicq; }
    32             set { _Oicq = value; }
    33         }
    34         #endregion
    35 
    36         #region HelloStephen
    37         public string MyCNBlogs { get; set; }
    38         #endregion
    39 
    40         #region Method
    41         public static HelloStephen FindStephen(IHelloStephen stephen)
    42         {
    43             return (HelloStephen)stephen ?? new HelloStephen() { IsAlive = false };
    44         }
    45         #endregion
    46     }

      

    3.方法组转换。

      C#1中如果要创建一个委托实例,就必须同时指定委托类型和要采取的操作;C#2提供从方法组(一个方法名,表达式)到一个兼容类型的隐式转换。

    1 // C#1
    2 Thread t = new Thread(new ThreadStart(MyMethod));
    3 
    4 // C#2
    5 Thread t = new Thread(MyMethod);
    View Code

    4.C#1与C#2中委托协变逆变的不同之处。

       存在不兼容的风险:

    1 EventHandler general = new EventHandler(Event);
    2 KeyPressEventHandler key = new KeyPressEventHandler (general); // C#1报错,而C#2不报错。

    C#1要求两个委托的类型签名必须匹配。

     本文多数实例均引自《C# in Depth Second Edition》

  • 相关阅读:
    C#中静态变量 静态类 静态成员
    C#类型转换
    ASP.NET页面跳转及传值方式
    ADO.NET
    C#方法的参数类型
    Oracle %TYPE 和 %ROWTYPE
    静态页面与动态页面
    相对路径和绝对路径
    45 个非常有用的 Oracle 查询语句(转)
    ASP.NET MVC 之表格分页
  • 原文地址:https://www.cnblogs.com/cuiyansong/p/3102974.html
Copyright © 2011-2022 走看看