协变与逆变
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
总结…
逆变: 对于接口而言 是传入一个对象 (in)关键字 ,它允许将 父类型泛型接口赋值给 子类型泛型接口 ,例如 例子中的ISetDateServices <in T>, 但是由于是 传入类型约束,所以是安全的…因为调用方法的时候 方法接收的参数为父类型.
协变: 对于接口而言 是传出 一个对象(out)关键字,他允许 将 子类型泛型赋值给父类型,例如例子中的 IGetDateServices<out T> , 传出类型被约束为父类型,实际传出的类型为子类型.
总体来说 都是 多态的提现 都是 将 父类变量指向子类型实例的值. 例如:object o=1; 只不过 逆变 多绕了一下. 而协变就好理解一些