1.可变型的类型: 协变和逆变
可变性是以一种类型安全的方式,将一个对象当做另一个对象来使用。如果不能将一个类型替换为另一个类型,那么这个类型就称之为:不变量。协变和逆变是两个相互对立的概念:
- 如果某个返回的类型可以由其派生类型替换,那么这个类型就是支持协变的
- 如果某个参数类型可以由其基类替换,那么这个类型就是支持逆变的。
简介: 在C# 4.0之前都是不存在协变型和逆变型,之前都是属于“不变量”. 也就是说。 协变和逆变 是在C#4.0之后出现的
关键字:out 和 in分别是对应着协变和逆变.
- out: 用于作为输出结果和返回参数 使用的
- in: 用于传入参数 和输出的结果类型不和传入参数相对应的
例子:
IEnumerable 这个类型是为指定的类型进行迭代 定义导航(F12) 后你会发现 是下面这种情况
public interface IEnumerable : IEnumerable
IEnumerator 这个类型是指 支持在泛型集合上进行简单的迭代。 定义导航 (F12) 后同上。
Action 对应的是
//Action 委托的定义(支持逆变)
public delegate void Action(T obj);
====================接下来讲解下C# 可变型的限制。
1. 不支持类的类型参数的可变性
只有接口和委托可以拥有可变的类型参数。in 和 out 修饰符只能用来修饰泛型接口和泛型委托。
2. 可变性只支持引用转换
可变性只能用于引用类型,禁止任何值类型和用户定义的转换,如下面的转换是无效的:
将 IEnumerable 转换为 IEnumerable ——装箱转换
将 IEnumerable 转换为 IEnumerable ——值类型转换
将 IEnumerable 转换为 IEnumerable ——用户定义的转换
3. 类型参数使用了 out 或者 ref 将禁止可变性
对于泛型类型参数来说,如果要将该类型的实参传给使用 out 或者 ref 关键字的方法,便不允许可变性,如:
delegate void someDelegate(ref T t)
这段代码编译器会报错。
4. 可变性必须显式指定
从实现上来说编译器完全可以自己判断哪些泛型参数能够逆变和协变,但实际却没有这么做,这是因为C#的开发团队认为:
必须由开发者明确的指定可变性,因为这会促使开发者考虑他们的行为将会带来什么后果,从而思考他们的设计是否合理。
5. 注意破坏性修改
在修改已有代码接口的可变性时,会有破坏当前代码的风险。例如,如果你依赖于不允许可变性的is或as操作符的结果,运行在.NET 4时,代码的行为将有所不同。同样,在某些情况下,因为有了更多可用的选项,重载决策也会选择不同的方法。所以在对已有代码引入可变性时要做好足够的单元测试以及防御措施。
6. 多播委托与可变性不能混用
下面的代码能够通过编译,但是在运行时会抛出 ArgumentException 异常:
Func stringFunc = () => “”;
Func objectFunc = () => new object();
Func combined = objectFunc + stringFunc;
这是因为负责链接多个委托的 Delegate.Combine方法要求参数必须为相同的类型。上面的示例我们可以修改成如下正确的代码:
Func stringFunc = () => “”;
Func defensiveCopy = new Func(stringFunc);
Func objectFunc = () => new object();
Func combined = objectFunc + defensiveCopy;