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》

  • 相关阅读:
    python中的编码问题
    CVPR2018 Tutorial 之 Visual Recognition and Beyond
    hdu 1376 Octal Fractions
    hdu 1329 Hanoi Tower Troubles Again!
    hdu 1309 Loansome Car Buyer
    hdu 1333 Smith Numbers
    hdu 1288 Hat's Tea
    hdu 1284 钱币兑换问题
    hdu 1275 两车追及或相遇问题
    hdu 1270 小希的数表
  • 原文地址:https://www.cnblogs.com/cuiyansong/p/3102974.html
Copyright © 2011-2022 走看看