回顾C#发展的历史,C#1.0完全是模仿Java,并保留了C/C++的一些特性如struct,新学者很容易上手;C#2.0加入了泛型,也与Java1.5的泛型如出一辙;C#3.0加入了一堆语法糖,并在没有修改CLR的情况下引入了Linq,简直是神来之笔,虽然很多项目出于各种各样如性能之类的原因没有采用,但非常适合小型程序的快速开发,减轻了程序员的工作量,也提高了代码的可读性;C#4.0增加了动态语言的特性,从里面可以看到很多javascript、python这些动态语言的影子。虽然越来越偏离静态语言的道路,但从另一个角度来说,这些特性也都是为了提高程序员的生产力。至于被接受与否,还是让时间来说话吧。
PS:这里面还有一点版本号的小插曲――VS2008所对应的.Net Framework是3.5,C#是3.0,CLR是2.0,及其混乱,MS终于下决心在VS2010中把这三个版本号都统一成了4.0,于是CLR3不知所终……
Dynamically Typed Object
C#4.0加入了dynamic关键字,可以申明一个变量的static类型为dynamic(有点绕口)。
在3.0及之前,如果你不知道一个变量的类型,而要去调用它的一个方法,一般会用到反射:
![](http://926579.blog.51cto.com/images/editer/InBlock.gif)
object calc = GetCalculator();
![](http://926579.blog.51cto.com/images/editer/InBlock.gif)
Type calcType = calc.GetType();
![](http://926579.blog.51cto.com/images/editer/InBlock.gif)
object res = calcType.InvokeMember( "Add",
![](http://926579.blog.51cto.com/images/editer/InBlock.gif)
BindingFlags.InvokeMethod, null,
![](http://926579.blog.51cto.com/images/editer/InBlock.gif)
new object[] { 10, 20 });
![](http://926579.blog.51cto.com/images/editer/InBlock.gif)
int sum = Convert.ToInt32(res);
有了dynamic,就可以把上面代码简化为:
![](http://926579.blog.51cto.com/images/editer/InBlock.gif)
dynamic calc = GetCalculator();
![](http://926579.blog.51cto.com/images/editer/InBlock.gif)
int sum = calc.Add(10, 20);
使用dynamic的好处在于,可以不去关心对象是来源于COM, IronPython, HTML DOM或者反射,只要知道有什么方法可以调用就可以了,剩下的工作可以留给runtime。下面是调用IronPython类的例子:
![](http://926579.blog.51cto.com/images/editer/InBlock.gif)
ScriptRuntime py = Python.CreateRuntime();
![](http://926579.blog.51cto.com/images/editer/InBlock.gif)
dynamic helloworld = py.UseFile( "helloworld.py");
![](http://926579.blog.51cto.com/images/editer/InBlock.gif)
Console.WriteLine( "helloworld.py loaded!");
dynamic也可以用在变量的传递中,runtime会自动选择一个最匹配的overload方法。
这里有一个demo:把一段javascript代码拷到C#文件中,将var改成dynamic,function改成void,再改一下构造函数的调用方式(new type()改为win.New.type()),去掉javascript中的win.前缀(因为这已经是C#的方法了),就可以直接运行了。
dynamic的实现是基于IDynamicObject接口和DynamicObject抽象类。而动态方法、属性的调用都被转为了GetMember、Invoke等方法的调用。
![](http://926579.blog.51cto.com/images/editer/InBlock.gif)
public abstract class DynamicObject : IDynamicObject
![](http://926579.blog.51cto.com/images/editer/InBlock.gif)
{
![](http://926579.blog.51cto.com/images/editer/InBlock.gif)
public virtual object GetMember(GetMemberBinder info);
![](http://926579.blog.51cto.com/images/editer/InBlock.gif)
public virtual object SetMember(SetMemberBinder info, object value);
![](http://926579.blog.51cto.com/images/editer/InBlock.gif)
public virtual object DeleteMember(DeleteMemberBinder info); public virtual object UnaryOperation(UnaryOperationBinder info);
![](http://926579.blog.51cto.com/images/editer/InBlock.gif)
public virtual object BinaryOperation(BinaryOperationBinder info, object arg);
![](http://926579.blog.51cto.com/images/editer/InBlock.gif)
public virtual object Convert(ConvertBinder info); public virtual object Invoke(InvokeBinder info, object[] args);
![](http://926579.blog.51cto.com/images/editer/InBlock.gif)
public virtual object InvokeMember(InvokeMemberBinder info, object[] args);
![](http://926579.blog.51cto.com/images/editer/InBlock.gif)
public virtual object CreateInstance(CreateInstanceBinder info, object[] args); public virtual object GetIndex(GetIndexBinder info, object[] indices);
![](http://926579.blog.51cto.com/images/editer/InBlock.gif)
public virtual object SetIndex(SetIndexBinder info, object[] indices, object value);
![](http://926579.blog.51cto.com/images/editer/InBlock.gif)
public virtual object DeleteIndex(DeleteIndexBinder info, object[] indices); public MetaObject IDynamicObject.GetMetaObject();
![](http://926579.blog.51cto.com/images/editer/InBlock.gif)
}
Named and optional parameters
这似乎不是什么很难实现或很新颖的特性,只要编译器的支持就可以(VB很早就支持了)。估计加入的原因是群众的呼声太高了。
带有可选参数方法的声明:
![](http://926579.blog.51cto.com/images/editer/InBlock.gif)
public StreamReader OpenTextFile(
![](http://926579.blog.51cto.com/images/editer/InBlock.gif)
string path,
![](http://926579.blog.51cto.com/images/editer/InBlock.gif)
Encoding encoding = null,
![](http://926579.blog.51cto.com/images/editer/InBlock.gif)
bool detectEncoding = true,
![](http://926579.blog.51cto.com/images/editer/InBlock.gif)
int bufferSize = 1024);
命名参数必须在最后使用:
![](http://926579.blog.51cto.com/images/editer/InBlock.gif)
OpenTextFile( "foo.txt", Encoding.UTF8, bufferSize: 4096);
顺序不限:
![](http://926579.blog.51cto.com/images/editer/InBlock.gif)
OpenTextFile(bufferSize: 4096, path: "foo.txt", detectEncoding: false);
Improved COM Interoperability
在C#中在调用COM对象如office对象时,经常需要写一堆不必要的参数:
![](http://926579.blog.51cto.com/images/editer/InBlock.gif)
object fileName = "Test.docx";
![](http://926579.blog.51cto.com/images/editer/InBlock.gif)
object missing = System.Reflection.Missing.Value;
![](http://926579.blog.51cto.com/images/editer/InBlock.gif)
doc.SaveAs( ref fileName,
![](http://926579.blog.51cto.com/images/editer/InBlock.gif)
ref missing, ref missing, ref missing,
![](http://926579.blog.51cto.com/images/editer/InBlock.gif)
ref missing, ref missing, ref missing,
![](http://926579.blog.51cto.com/images/editer/InBlock.gif)
ref missing, ref missing, ref missing,
![](http://926579.blog.51cto.com/images/editer/InBlock.gif)
ref missing, ref missing, ref missing,
![](http://926579.blog.51cto.com/images/editer/InBlock.gif)
ref missing, ref missing, ref missing);
4.0中就可以直接写成:
![](http://926579.blog.51cto.com/images/editer/InBlock.gif)
doc.SaveAs( "Test.docx");
C#4.0对COM交互做了下面几方面的改进:
![](http://926579.blog.51cto.com/images/editer/InBlock.gif)
Automatic object -> dynamic mapping
![](http://926579.blog.51cto.com/images/editer/InBlock.gif)
Optional and named parameters
![](http://926579.blog.51cto.com/images/editer/InBlock.gif)
Indexed properties
![](http://926579.blog.51cto.com/images/editer/InBlock.gif)
Optional “ ref” modifier
![](http://926579.blog.51cto.com/images/editer/InBlock.gif)
Interop type embedding (“No PIA”)
对第1点和第5点的简单解释如下:
在COM调用中,很多输入输出类型都是object,这样就必须知道返回对象的确切类型,强制转换后才可以调用相应的方法。在4.0中有了dynamic的支持,就可以在导入这些COM接口时将变量定义为dynamic而不是object,省掉了强制类型转换。
PIA(Primary Interop Assemblies)是根据COM API生成的.Net Assembly,一般体积比较大。在4.0中运行时不需要PIA的存在,编译器会判断你的程序具体使用了哪一部分COM API,只把这部分用PIA包装,直接加入到你自己程序的Assembly里面。
Co- and Contra-Variance
实在是不知道怎么翻译这两个词。
(感谢Ariex,徐少侠,AlexChen的提示,应翻译为协变和逆变, http://msdn.microsoft.com/zh-cn/library/ms173174(VS.80).aspx)
在C#中,下面的类型转换是非法的:
![](http://926579.blog.51cto.com/images/editer/InBlock.gif)
IList< string> strings = new List< string>();
![](http://926579.blog.51cto.com/images/editer/InBlock.gif)
IList< object> objects = strings;
因为你有可能会这样做,而编译器的静态检查无法查出错误:
![](http://926579.blog.51cto.com/images/editer/InBlock.gif)
objects[0] = 5;
![](http://926579.blog.51cto.com/images/editer/InBlock.gif)
string s = strings[0];
4.0中在声明generic的Interface及Delegate时可以加in及out关键字,如:
![](http://926579.blog.51cto.com/images/editer/InBlock.gif)
public interface IEnumerable< out T> : IEnumerable
![](http://926579.blog.51cto.com/images/editer/InBlock.gif)
{
![](http://926579.blog.51cto.com/images/editer/InBlock.gif)
IEnumerator<T> GetEnumerator();
![](http://926579.blog.51cto.com/images/editer/InBlock.gif)
}
![](http://926579.blog.51cto.com/images/editer/InBlock.gif)
public interface IEnumerator< out T> : IEnumerator
![](http://926579.blog.51cto.com/images/editer/InBlock.gif)
{
![](http://926579.blog.51cto.com/images/editer/InBlock.gif)
bool MoveNext();
![](http://926579.blog.51cto.com/images/editer/InBlock.gif)
T Current { get; }
![](http://926579.blog.51cto.com/images/editer/InBlock.gif)
}
![](http://926579.blog.51cto.com/images/editer/InBlock.gif)
public interface IComparer< in T>
![](http://926579.blog.51cto.com/images/editer/InBlock.gif)
{
![](http://926579.blog.51cto.com/images/editer/InBlock.gif)
public int Compare(T left, T right);
![](http://926579.blog.51cto.com/images/editer/InBlock.gif)
}
out关键字的意思是说IEnumerable<T>中T只会被用在输出中,值不会被改变。这样将IEnumerable<string>转为IEnumerable<object>类型就是安全的。
in的意思正好相反,是说IComparer<T>中的T只会被用在输入中,这样就可以将IComparer<object>安全的转为IComparer<string>类型。
前者被称为Co-Variance, 后者就是Contra-Variance。
.Net4.0中使用out/in声明的Interface:
![](http://926579.blog.51cto.com/images/editer/InBlock.gif)
System.Collections.Generic.IEnumerable< out T>
![](http://926579.blog.51cto.com/images/editer/InBlock.gif)
System.Collections.Generic.IEnumerator< out T>
![](http://926579.blog.51cto.com/images/editer/InBlock.gif)
System.Linq.IQueryable< out T>
![](http://926579.blog.51cto.com/images/editer/InBlock.gif)
System.Collections.Generic.IComparer< in T>
![](http://926579.blog.51cto.com/images/editer/InBlock.gif)
System.Collections.Generic.IEqualityComparer< in T>
![](http://926579.blog.51cto.com/images/editer/InBlock.gif)
System.IComparable< in T>
![](http://926579.blog.51cto.com/images/editer/InBlock.gif)
Delegate:
![](http://926579.blog.51cto.com/images/editer/InBlock.gif)
System.Func< in T, …, out R>
![](http://926579.blog.51cto.com/images/editer/InBlock.gif)
System.Action< in T, …>
![](http://926579.blog.51cto.com/images/editer/InBlock.gif)
System.Predicate< in T>
![](http://926579.blog.51cto.com/images/editer/InBlock.gif)
System.Comparison< in T>
![](http://926579.blog.51cto.com/images/editer/InBlock.gif)
System.EventHandler< in T>
Compiler as a Service
4.0中增加了与编译器相关的API,这样就可以将字符串作为代码动态编译执行,跟javascript好像。
Video的最后,Anders做了一个很酷的demo,大概只用了二三十行代码,就实现了在控制台中直接执行C#语句,定义并调用函数,动态创建windows form,添加button等功能,看起来完全不逊色于Python,Ruby之类语言的控制台。
沉寂了n年之后,CLR终于要出新版本了,这回Jeffrey Richter大侠没有借口不出新版的CLR via C#了吧:)
Reference:
视频: http://channel9.msdn.com/pdc2008/TL16/
PPT: http://mschnlnine.vo.llnwd.net/d1/pdc08/PPTX/TL16.pptx
示例代码及文档(New features in C# 4.0): http://code.msdn.microsoft.com/csharpfuture