c# 4.0新特性一览

终于静下心来仔细听了一遍Anders Hejlsberg(Visual Studio组的TECHNICAL FELLOW,C#的设计者之一)在PDC08上讲的“The Future of C#”(http://channel9.msdn.com/pdc2008/TL16/)。

回顾C#发展的历史,C#1.0完全是模仿Java,并保留了C/C++的一些特性如struct,新学者很容易上手;C#2.0加入了泛型,也与Java1.5的泛型如出一辙;C#3.0加入了一堆语法糖,并在没有修改CLR的情况下引入了Linq,简直是神来之笔,虽然很多项目出于各种各样如性能之类的原因没有采用,但非常适合小型程序的快速开发,减轻了程序员的工作量,也提高了代码的可读性;C#4.0增加了动态语言的特性,从里面可以看到很多javascript、python这些动态语言的影子。虽然越来越偏离静态语言的道路,但从另一个角度来说,这些特性也都是为了提高程序员的生产力。至于被接受与否,还是让时间来说话吧。

image

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及之前,如果你不知道一个变量的类型,而要去调用它的一个方法,一般会用到反射:

object calc = GetCalculator();
Type calcType = calc.GetType();
object res = calcType.InvokeMember("Add",
BindingFlags.InvokeMethod, null,calc
new object[] { 10, 20 });
int sum = Convert.ToInt32(res);

有了dynamic,就可以把上面代码简化为:

dynamic calc = GetCalculator();
int sum = calc.Add(10, 20);

使用dynamic的好处在于,可以不去关心对象是来源于COM, IronPython, HTML DOM或者反射,只要知道有什么方法可以调用就可以了,剩下的工作可以留给runtime。下面是调用IronPython类的例子:

ScriptRuntime py = Python.CreateRuntime();
dynamic helloworld = py.UseFile("helloworld.py");
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等方法的调用。

public abstract class DynamicObject : IDynamicObject
{
public virtual object GetMember(GetMemberBinder info);
public virtual object SetMember(SetMemberBinder info, object value);
public virtual object DeleteMember(DeleteMemberBinder info);     public virtual object UnaryOperation(UnaryOperationBinder info);
public virtual object BinaryOperation(BinaryOperationBinder info, object arg);
public virtual object Convert(ConvertBinder info);     public virtual object Invoke(InvokeBinder info, object[] args);
public virtual object InvokeMember(InvokeMemberBinder info, object[] args);
public virtual object CreateInstance(CreateInstanceBinder info, object[] args);     public virtual object GetIndex(GetIndexBinder info, object[] indices);
public virtual object SetIndex(SetIndexBinder info, object[] indices, object value);
public virtual object DeleteIndex(DeleteIndexBinder info, object[] indices);     public MetaObject IDynamicObject.GetMetaObject();
}

 

Named and optional parameters

这似乎不是什么很难实现或很新颖的特性,只要编译器的支持就可以(VB很早就支持了)。估计加入的原因是群众的呼声太高了。

带有可选参数方法的声明:

public StreamReader OpenTextFile(
string path,
Encoding encoding = null,
bool detectEncoding = true,
int bufferSize = 1024);

命名参数必须在最后使用:

OpenTextFile("foo.txt", Encoding.UTF8, bufferSize: 4096);

顺序不限:

OpenTextFile(bufferSize: 4096, path: "foo.txt", detectEncoding: false);

Improved COM Interoperability

在C#中在调用COM对象如office对象时,经常需要写一堆不必要的参数:

object fileName = "Test.docx";
object missing  = System.Reflection.Missing.Value;
doc.SaveAs(ref fileName,
ref missing, ref missing, ref missing,
ref missing, ref missing, ref missing,
ref missing, ref missing, ref missing,
ref missing, ref missing, ref missing,
ref missing, ref missing, ref missing);

4.0中就可以直接写成:

doc.SaveAs("Test.docx");

C#4.0对COM交互做了下面几方面的改进:

  1. Automatic object -> dynamic mapping
  2. Optional and named parameters
  3. Indexed properties
  4. Optional “ref” modifier
  5. 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#中,下面的类型转换是非法的:

IList<string> strings = new List<string>();
IList<object> objects = strings;

因为你有可能会这样做,而编译器的静态检查无法查出错误:

objects[0] = 5;
string s = strings[0];

4.0中在声明generic的Interface及Delegate时可以加in及out关键字,如:

public interface IEnumerable<out T> : IEnumerable
{
IEnumerator<T> GetEnumerator();
}
public interface IEnumerator<out T> : IEnumerator
{
bool MoveNext();
T Current { get; }
}
public interface IComparer<in T>
{
public int Compare(T left, T right);
}

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:

System.Collections.Generic.IEnumerable<out T>
System.Collections.Generic.IEnumerator<out T>
System.Linq.IQueryable<out T>
System.Collections.Generic.IComparer<in T>
System.Collections.Generic.IEqualityComparer<in T>
System.IComparable<in T>

Delegate:

System.Func<in T, …, out R>
System.Action<in T, …>
System.Predicate<in T>
System.Comparison<in T>
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:

  1. 视频: http://channel9.msdn.com/pdc2008/TL16/
  2. PPT:http://mschnlnine.vo.llnwd.net/d1/pdc08/PPTX/TL16.pptx
  3. 示例代码及文档(New features in C# 4.0):http://code.msdn.microsoft.com/csharpfuture

Anders谈C# 4.0:新功能和展望

1. dynamic/IDynamicObject

这个改进使得C#向动态语言又进了一步,虽然C#并不会变成类似Perl/Python之类的动态语言(因为Anders认为静态语言所支持的一些特性比如Intellisense,类型检查等等是相当有用的),但是这并不代表C#不应该对动态特性提供更好的支持。从我们Interop的角度来看,dynamic比较类似COM中的IDispatch,也就是动态的根据提供的函数/属性名字动态选择匹配的动作并执行之,只不过这个接口现在变成了IDynamicObject。写法也很类似VB6。

原来要写:

   1: object obj = GetObject();  

   2: obj.GetType().InvokeMember(“CallSomeFunc”, …., new object[] { 1 });
现在只需:

   1: dynamic obj = GetObject();

   2: obj.CallSomeFunc(1); // obj通过IDynamicObject接口,支持CallSomeFunc方法。

   3:  // 相当于: (obj as IDynamicObject).Invoke(new InvokeBinder(“CallSomeFunc”), new object[] { 1 } );
这一切都是通过IDynamicObject接口实现的。只要对象支持IDynamicObject,那么任意对象都可以通过这种方式来直接调用,不管是COM,Python,JavaScript,等等。这个功能感觉基本上就是定义一个接口,然后编译器再把代码翻译一下就好了,关键还是各种对象的支持。

2. Optional Parameter / Named Parameters

以前C#特意不支持的可选参数终于现在可以支持了。命名参数也可以支持了,使用参数加冒号:

   1: OpenTextFile(“foo.txt”, Encoding.UTF8, bufferSize:123)
3. Improved COM Interoperability

3.1 Automatic object –> dynamic mapping

原来返回object的地方,现在object可以自动被视为dynamic。因此,以前需要cast的地方现在可以省去cast了,反正dynamic对象可以通过IDynamicObject来间接调用IDispatch接口(我觉得应该还是通过MemberInfo.Invoke来间接调用IDispatch,但是暂时没有时间验证其实现方式)来自动调用对应的函数,而不需要cast到对应的interface再调用。

原来是:(Range)excel.Cells[1, 1].Value = xxx;

现在可以写成:excel.Cells[1,1].Value = xxx; // call IDynamicObject.SetMember(“Value”, xxx);

3.2 Optional and named parameters

这个无需多说了吧。BTW,现在TlbImp的结果中(也就是Interop Assembly)已经在Metadata包含了缺省值,只是C#不用而已,现在C#可以直接使用了。

3.3 Indexed Property

这个Anders一句话带过,暂时不清楚具体是什么改进。

3.4 Optional ref modifier

在COM Interop时候可以不用写ref。具体的Anders也没有多谈。觉得应该是很小的改动。

3.5 Interop Type Embedding (NO PIA)

这个也就是之前我在前一篇提到的Type Equvalency。原来为了保证同一个COM接口具有相同的托管类型(因为对于同一个COM接口可以有多个对应的托管的接口),推荐使用PIA(Primary Interop Assembly)。但是,在使用PIA的过程中,发现PIA有不少问题,因此CLR Interop的某位牛人Architect想出了这个新Idea:不使用PIA,而是允许对应同一COM接口的不同托管接口之间可以互换使用,无需Cast,CLR内部将它们等价看待。这是一个比较大的改动,不管是对于编译器,还是CLR。更多的细节我会在以后的Blog中分若干次详细介绍。

4. Co-Variance & Contra-Variance

这个相对难以理解一些。Co-Variance和Contra-Variance在这系列文章中有讲述:http://blogs.msdn.com/ericlippert/archive/tags/Covariance+and+Contravariance/default.aspx,此人是C#编译器的Dev,自然有比较权威的解释,而且这个系列有N篇文章,讲的比较复杂。有空我会把这块内容详细在Blog中解释一下。

简单来讲,Co-Variant表示某模板参数用作传出,也即是函数的返回值或者out参数,等等,用关键字out表明:

   1: public interface IEnumerator<out T> // Co-Variant

   2: {

   3:     T Current { get; }

   4:     bool Next(); 

   5: }

   6: 

   7: IEnumerator<string> strings = GetStrings();

   8: IEnumerator<object> objects = strings; // 这个OK,反之报错
在上面的转换,意味着IEnumerator<string>可以被视作IEnumerator<object>,因为任何使用IEnumerator<object>.Current的地方都会认为Current返回Object,而string可以被视作Object。

反之,Contra-Variant表示模板参数T只能被用在函数入参或者属性的入参,用in表示:

   1: public interface IComparer<in T> // Contra-Variant

   2: {

   3:     int Compare(T x, T y);

   4: }

   5:

   6: IComparer<object> objComp = GetComparer();

   7: IComparer<string> strComp = objComp; // 这个OK,反之报错
类似的,任何使用IComparer<string>.Compare(string x, string y)的地方都会传入string,自然会被IComparer<object>.Compare(object x, object y)所接受。这个Fetaure是对于目前的Generic的一个补充,不过这个Feature目前只对Interface和Delegate有效。

5. C# 5.0???

5.1 Meta-Programming Capabilities

学习Ruby On Rails,引入强大的Meta-Programming能力。

5.2 Compiler As Service

用托管代码重写编译器(原来是用C++编写的,使用IMetaDataEmit接口输出PE文件),支持其他人编写代码参与整个编译过程(估计类似Plugin)或者调用编译过程(类似已有的动态语言的eval功能)。

Anders演示了通过CSharpEvaluator直接编译用string表示的C#代码然后直接执行:

   1: CSharpEvaluator ev = new CSharpEvaluator();   2: ev.Eval(“for (int i = 0; i < 10; i++) { … }");
通过这个就可以很容易写一个C#的Shell了:

C#> int Add(int a, int b) { return a+b; }

C#> Add(1, 2)

3

C#> Form = new Form() { Text = “Hello World” };

这个基本上是原来的动态语言的强项,现在C#也可以做了!

有兴趣的朋友可以在下面找到这个Talk:http://channel9.msdn.com/pdc2008/TL16/。在线和下载都有。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值