C#4.0
动态绑定
C#4.0
引入了一个新的关键字dynamic
,用来表示动态类型。dynamic
的出现让C#具有了弱语言类型的特性。
关于dynamic的主要规则:
- 几乎所有CLR类型都可以隐式转换为dynamic
- 所有dynamic类型的表达式都可以隐式转换为CLR类型
- 使用dynamic类型值得表达式通常会动态的求值
- 动态求值表达式的静态类型通常被视为dynamic
编译器对待dynamic
的方式与普通的CLR类型不同。任何使用了动态值的表达式都会从根本上改变编译器的行为。编译器不会试图弄懂代码的确切含义,不会恰当的绑定各个成员的访问,不会执行重载决策。它只是通过解析源代码,找出要执行的操作的种类、名称、所设计的参数以及其他相关信息。编译器也不会发出(emit)IL来直接执行代码,而是使用所有必要的信息生成调用DLR的代码。剩下的工作将在执行时进行。
DLR(Dynamic Language Runtime),动态语言运行时,是所有动态语言和C#编译器用来动态执行代码的库。
DLR虽然以运行时为名,但与CLR不在同一个级别(它不涉及JIT编译、本地API奉送、GC等内容)。而是建立在大量.NET 2.0
和 .NET 3.5
的功能之上,特别是DynamicMethod
和Expression
类型。.NET4
还扩展了表达式树的API,DLR可以使用它来表示更多的概念。
尽管DLR不直接操作本地代码,但在在某种程度上我们可以认为它做着与CLR类似的工作:正如CLR将IL转换为本地代码一样,DLR将用绑定器、调用点、元对象以及其他各种概念表示的代码转换为表达式树,然后将表达式树编译为IL,最终由CLR编译为本地代码。
不要混淆dynamic
和var
。用var
声明局部变量只是一种简化语法,它要求编译器根据表达式推断出具体数据类型。var
关键字只能在方法内部声明局部变量,而dynamic
关键字可用于局部变量、字段和参数。表达式不能转型为var
,但能转型为dynamic
。必须显式初始化用var
声明的变量,但无需初始化用dynamic
声明的变量。
命名实参/可选参数
命名实参
named argument
有了命名实参,你将不再需要记住或查找形参在所调用方法的形参列表中的顺序。 每个实参的形参都可按形参名称进行指定。
SetName(firstName:"first",middleName:‘middle‘,LastName:‘last‘);
//可以按任意顺序设置实参
SetName(LastName:‘last‘,firstName:"first",middleName:‘middle‘);
可选参数
optional parameter
方法、构造函数、索引器或委托的定义可以指定其形参为必需还是可选。 任何调用都必须为所有必需的形参提供实参,但可以为可选的形参省略实参。
每个可选形参都有一个默认值作为其定义的一部分。 如果没有为该形参发送实参,则使用默认值。
可选参数意味着一些参数是可选的,调用者不必显式指定它们的值,但是必须给可选参数一个默认值。默认值必须为数字或字符串字面量、null、const常量、枚举成员和default(T)操作符。
可选参数必须出现在必需参数之后。
命名实参常常与可选参数同时出现。
public void ExampleMethod(int required, string optionalstr = "default string",
int optionalint = 10)
{
}
//调用
anExample.ExampleMethod(3, optionalint: 4);
泛型协变和逆变
可变性是指以一种类型安全的方式,将一个对象作为另一个对象来使用。
可变性分为两种:
- 协变性(Covariance):指返回类型的兼容性,用于向调用者返回某项操作的值,能够使用比原始指定的派生类型的派生程度更大(更具体的)的类型。
- 逆变性(Contravariance):指参数的兼容性,用于调用者向某项操作传入的值,指能够使用比原始指定的派生类型的派生程度更小(不太具体的,更抽象的)的类型。
可变性摘要:
- 在
.NET Framework 4
中,可变性参数仅限于泛型接口
和泛型委托类型
。 - 泛型接口或泛型委托类型可以同时具有协变和逆变类型参数。
- 可变性参数仅适用于引用类型;如果为可变性参数指定值类型,则该类型参数对于生成的构造类型是不变的。
- 可变性参数不适用于委托组合。
在泛型接口或委托的声明中,C#4使用out
修饰符来指定类型参数的协变性,使用in
修饰符来指定逆变性。声明完成后,就可以对相关的类型进行隐式转换了。
如果有一个泛型参数标记为out
,则代表它是用来输出的,只能作为结果返回;而如果有一个泛型参数标记为in
,则代表它是用来输入的,只能作为参数传入。
可变性的转换是引用转换:任何使用了协变和逆变的转换都是引用转换,这意味着转换之后将返回相同的引用。它不会创建新的对象,只是认为现有的引用与目标类型匹配。
我们熟知的Action<T>
和Func<T>
就分别使用了逆变跟协变。
//泛型参数具有逆变性
public delegate void Action<in T>(T obj);
public interface IComparer<in T>
{
int Compare(T x, T y);
}
//泛型参数具有协变性
public delegate T Func<out T>();
public interface IEnumerable<out T> : IEnumerable
{
new IEnumerator<T> GetEnumerator();
}
//**************逆变性***************
//明明object不可以转换为string,为啥strComparer可以接收IComparer<object>类型的变量?
//是因为IComparer<in T>的泛型参数具有逆变性
//所以IComparer<string>接口可以使用比string更抽象的object类型
IComparer<object> objComparer = null;
IComparer<string> strComparer = objComparer;
//同理,因为Action<in T>的泛型参数具有逆变性
//所以Action<string>委托可以使用比string更抽象的object类型
Action<object> objAction = (obj) => Console.WriteLine(obj.GetHashCode());
Action<string> strAction = objAction;
//**************协变性***************
//由于IEnumerable<out T>的泛型参数具有协变性
//所以IEnumerable<object>接口可以使用比object更具体的string类型
IEnumerable<string> strEnumerable = new List<string> { };
IEnumerable<object> objEnumerable = strEnumerable;
//同理,因为Func<out T>的泛型参数具有协变性
//所以Func<object>委托可以使用比object更具体的string类型
Func<string> strFunc = () => { return ""; };
Func<object> objFunc = strFunc;
嵌入的互操作类型
从 .NET Framework 4
开始,公共语言运行时支持将 COM
类型的类型信息直接嵌入到托管程序集中,而不要求托管程序集从互操作程序集中获取 COM
类型的类型信息。 由于嵌入式类型信息仅包含托管程序集实际使用的类型和成员,因此两个托管程序集可能具有相同 COM
类型的不同视图。 每个托管程序集都有不同的 Type
对象来表示其 COM
类型视图。 公共语言运行时支持接口、结构、枚举和委托等不同视图之间的类型等效性。
类型等效性意味着从一个托管程序集传递到另一个托管程序集的 COM
对象可以转换为接收程序集中适当的托管类型。
备注:类型等效性和嵌入式互操作类型简化了使用
COM
组件的应用程序和加载项的部署,因为无需与应用程序一起部署互操作程序集。 如果共享COM
组件的开发人员希望较早版本的.NET Framework
使用其组件,他们仍须创建主互操作程序集 (PIA
)。