深入理解C#|04 C#4互操作性提升
动态类型
简而言之就是可以将代码绑定从编译器转移到代码执行期,可以写类似于脚本语言的方法,感觉没啥用,不记录了
可选形参和命名实参
带默认值的形参和带名字的实参
// 一个必须的形参X,以及两个可选的形参Y、Z
public void Method(int x,int y=1,int z=2)
{
...
}
此时当不输入形参YZ时候,会用默认值输入
默认形参必须放在必须形参之后,且必须符合以下要求之一
- 编译时常量,值类型,字符串,或者null
- default表达式
- new表达式,只对值类型有效
所以以下调用均合法
Method(1);
Method(1,10);
Method(1,10,100);
那么如何输入XZ而不输入Y呢?
命名实参
Method(1,z:100); // 此时Y会是默认值,而XZ则是输入值
COM互操作性提升
略
泛型型变
观察以下代码
IEnumerable<string> strings = new List<string>{"a","b","c"};
IEnumerable<object> objects = strings; // 合法
IList<string> strings = new List<string>{"a","b","c"};
IList<object> objects = strings; // 非法
原因在于,在IEnumerable<T>中,所有的T均用于输出
而在IList<T>中,T同时用于输入和输出
如果我们同意把IList<string>看做是IList<object>,那么如何理解以下代码
IList<string> strings = new List<string>{"a","b","c"};
IList<object> objects = strings;
objects.add(new object());
var str = strings[3];
此时会产生混淆,到底是string还是object?
再举一个例子,在委托中,泛型总是只用于输入,所以把Action<string>转为Action<object>是完全合法的,因为如果一个委托能接受object,那他一定能接受string,反过来也是同理
Action<object> objectAction = obj => Console.WriteLine(obj);
Action<string> stringAction = objectAction;
此时我们给出如下定义
- 协变:当泛型的值仅用于输出时
- 逆变:当泛型的值仅用于输入时
- 不变:同时用于输入和输出
接口与委托的变体语法
- 变体语法只能用于接口和委托
- 变体的定义和每一个具体的类型形参绑定,可以说IEnumerable<T>是协变的,但是更具体一点,应该说IEnumerable<T>对于T来说是协变的
此时对于接口和委托的申明,可以产生如下新语法
public interface IEnumerable<out T> // 协变
public delegate void Action<in T> // 逆变
public interface IList<T> // 不变