c#入门-协变,逆变

一个bug

这是两个泛型列表。你不用在意他是什么,你只要知道他和数组很像就行。

List<string> strList = new List<string>() { "123", "456", "789" };
List<object> objList = new List<object>(3);

现在把第二个列表的内容,用第一个列表的内容覆写。

for (int i = 0; i < strList.Count&&i<objList.Count; i++)
{
	objList[i]= strList[i];
}

把一个值赋值给不同类型的变量。但因为变量类型和值的类型有继承关系,所以可以成立。
那么既然string类型可以赋值给object类型,那你有没有想过不访问元素,直接从外面进行赋值。
像这样:

List<string> strList = new List<string>() { "123", "456", "789" };
List<object> objList objList = strList;

首先告诉你,这种做法在数组上是可以的,但是按照c#的类型安全理念,不应该成立。
因为会发生以下错误:

string[] strArr = new string[] { "123", "456", "789" };
object[] objArr = strArr;

objArr[0] = 12;

这在编译时是可以通过的,但实际运行起来,会出现类型转换失败的异常。

协变逆变

泛型具有严格的限制,像上面那种使用方式是不允许的。
而协变逆变就是用来解开这种限制。
而协变逆变也有限制,不能对类使用,只能对接口或委托使用。
(因为他们不会储存字段,只有方法)。

协变

协变:和谐的变化,儿子随着时间流逝会当父亲。泛型填的是子类,可以迎合父类。
使用out关键字修饰泛型占位符,表示输出。修饰的泛型占位符仅能作为返回类型。

interface IOut<out T> 
{
	T GetValue();
}
class COut : IOut<string>
{
	public string GetValue() => "hello";
}
IOut<string> out1 = new COut();
IOut<object> out2 = out1;

因为这个接口中只有输出的泛型。
在输出的时候把string作为object看待是没有问题的。

逆变

逆变:大逆不道的变化,要父亲当儿子。泛型填的是父类,却要迎合子类。
使用in关键字修饰泛型占位符,表示输入。修饰的泛型占位符仅能作为参数类型。

interface IIn<in T> {

	void SetValue(T value);
} 
class CIn : IIn<object>
{
	public void SetValue(object value) { }
}
IIn<object>in1=new CIn();
IIn<string> in2 = in1;

因为这个接口中只有参数的泛型,
在作为参数的时候,把string作为object看待是没有问题的。

而泛型类中的字段,是技能作为输入,又能作为输出的存在。
所以协变逆变对泛型类不可用。

你可能感兴趣的:(#,进阶部分,c#)