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》

 

你可能感兴趣的:(sha)