动态类型

简而言之就是可以将代码绑定从编译器转移到代码执行期,可以写类似于脚本语言的方法,感觉没啥用,不记录了

可选形参和命名实参

带默认值的形参和带名字的实参

// 一个必须的形参X,以及两个可选的形参Y、Z
public void Method(int x,int y=1,int z=2)
{
    ...
}

此时当不输入形参YZ时候,会用默认值输入

默认形参必须放在必须形参之后,且必须符合以下要求之一

  1. 编译时常量,值类型,字符串,或者null
  2. default表达式
  3. 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;

此时我们给出如下定义

  1. 协变:当泛型的值仅用于输出时
  2. 逆变:当泛型的值仅用于输入时
  3. 不变:同时用于输入和输出

接口与委托的变体语法

  1. 变体语法只能用于接口和委托
  2. 变体的定义和每一个具体的类型形参绑定,可以说IEnumerable<T>是协变的,但是更具体一点,应该说IEnumerable<T>对于T来说是协变的

此时对于接口和委托的申明,可以产生如下新语法

public interface IEnumerable<out T> // 协变
public delegate void Action<in T> // 逆变
public interface IList<T> // 不变