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提供从方法组(一个方法名,表达式)到一个兼容类型的隐式转换。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 // C#1 2 Thread t = new Thread(new ThreadStart(MyMethod)); 3 4 // C#2 5 Thread t = new Thread(MyMethod);
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》
作者:Stephen Cui
出处:http://www.cnblogs.com/cuiyansong
版权声明:文章属于本人及博客园共有,凡是没有标注[转载]的,请在文章末尾加入我的博客地址。
如果您觉得文章写的还不错,请点击“推荐一下”,谢谢。