小菜学习逆变与协变

协变与逆变

2013年10月9日19:00:00

关于接口的协变与逆变

  1     class Program
  2 
  3     {
  4 
  5         static void Main(string[] args)
  6 
  7         {
  8 
  9  
 10 
 11             //协变   IDataServices<Student>类型对象 变成 IDataServices<Person> 
 12 
 13             //泛型的子类 变成泛型的 父类 ,但是需要注意2个接口 不存在继承关系 只是编译器 允许这么写 CLR并提供支持,实际上是语法糖
 14 
 15             // 此处调用GetDate 返回值类型为Person;原因: 因为 gDS 传入 泛型的约束 是 Person 而这个方法 返回是 T 所以..
 16 
 17             //  这个比较好理解 例如 object a = "1";
 18 
 19             IGetDataServices<Person> gDS = GetDataServices();
 20 
 21             Person ans = gDS.GetData();    
 22 
 23             Console.WriteLine(ans);
 24 
 25            
 26 
 27             //逆变 将 泛型父类型复制给 泛型子类型 很诧异的方式 但是编译器确实允许这种存在
 28 
 29             // 同上 他们不存在 继承关系 编译器允许这样写 CLR底层 提供支持
 30 
 31             // SaveDate 方法 调用 是 安全的 因为逆变 只允许 传入对象是 ,类如Student,接受对象为Person, 类似:Person p=new Student,这明显就是多态的 子类实例被父类类型使用..
 32 
 33             // 例如说 string i= new object(); 这是错误的
 34 
 35             ISetDateServices<Student> sDs = SetDateServices();
 36 
 37             sDs.SaveData(ans as Student);
 38 
 39            
 40 
 41             //空格
 42 
 43             Console.ReadKey();
 44 
 45         }
 46 
 47  
 48 
 49         public static IGetDataServices<Student> GetDataServices()
 50 
 51         {
 52 
 53             return new StudentServices();
 54 
 55         }
 56 
 57  
 58 
 59         public static ISetDateServices<Person> SetDateServices()
 60 
 61         {
 62 
 63             return new PersonServices();
 64 
 65         }
 66 
 67     }
 68 
 69  
 70 
 71    
 72 
 73  
 74 
 75     public  class Person
 76 
 77     {
 78 
 79         public Person()
 80 
 81         {
 82 
 83            
 84 
 85         }
 86 
 87  
 88 
 89         public string Name { get; set; }
 90 
 91  
 92 
 93         public override string ToString()
 94 
 95         {
 96 
 97             return Name;
 98 
 99         }
100 
101     }
102 
103  
104 
105     public class Student:Person
106 
107     {
108 
109        public Student()
110 
111        {
112 
113           
114 
115        }
116 
117  
118 
119         public string SchoolName { set; get; }
120 
121  
122 
123        public override string ToString()
124 
125        {
126 
127            return base.ToString()+"|"+SchoolName;
128 
129        }
130 
131     }
132 
133  
134 
135     public interface IGetDataServices<out T>
136 
137     {
138 
139         T GetData();
140 
141     }
142 
143  
144 
145     public class StudentServices : IGetDataServices<Student>
146 
147     {
148 
149  
150 
151         public Student GetData()
152 
153         {
154 
155             return new Student()
156 
157             {
158 
159                Name = "Rufus",
160 
161                SchoolName = "Ms"
162 
163             };
164 
165         }
166 
167     }
168 
169  
170 
171     public interface ISetDateServices<in T>
172 
173     {
174 
175         void SaveData(T item);
176 
177     }
178 
179  
180 
181     public class PersonServices : ISetDateServices<Person>
182 
183     {
184 
185         public void SaveData(Person item)
186 
187         {
188 
189             Console.WriteLine("保存成功:" + item);
190 
191         }
192 
193 }
194 
195  
View Code

总结… 

逆变: 对于接口而言 是传入一个对象 (in)关键字 ,它允许将 父类型泛型接口赋值给 子类型泛型接口 ,例如 例子中的ISetDateServices <in T>, 但是由于是 传入类型约束,所以是安全的…因为调用方法的时候 方法接收的参数为父类型.

协变: 对于接口而言 是传出 一个对象(out)关键字,他允许 将 子类型泛型赋值给父类型,例如例子中的 IGetDateServices<out T> , 传出类型被约束为父类型,实际传出的类型为子类型.

总体来说 都是 多态的提现 都是 将 父类变量指向子类型实例的值. 例如:object o=1; 只不过 逆变 多绕了一下. 而协变就好理解一些

原文地址:https://www.cnblogs.com/rufus-hua/p/3360296.html