您的位置:首页 > 编程语言 > C#

VS2010开发体验系列之二 - 语言C#4.0

2010-05-06 15:28 369 查看
C#从1.0到4.0, 每次都会引入比较大的变化, 比如2.0的泛型,3.0的var,这次4.0也引入了一些新的东西,如下:

DLR(动态语言运行时)

命名参数和可选参数

特定于COM的互操作

协变和逆变

笔者首次尝试了这4个新特性,下面一一做简单的介绍:

1. DLR(动态语言运行时)

在CLR之上,.NET引入了一个叫DLR(Dynamic Language Runtime)的组件,这套组件提供了一系列的服务,用以支持动态语言。细分下来,主要是两方面:在静态语言类型中引入动态类型和支持动态语言比如IronRuby&IronPython. 以下这个架构图可以帮助您理解DLR。



刚才讲到, DLR提供了两个方面的支持,下面分别演示一下代码:

第一个方面,静态语言类型的动态支持,让我们看看一组代码,假设有三个类,Customer, VIP, Parterner.

public class Customer
{
public String Name { get; set; }
}
public class VIP : Customer
{
public String CustomerManager { get; set; }
}
public class Parterner
{
public String Name { get; set; }
public String Speciality { get; set; }
}


dynamic test = new VIP();
test.Name = "test";           // Compile Pass, Runtime Pass.
test.CustomerManager = "mgt"; // Compile Pass, Runtime Pass.
test.Speciality = "";       // Compile Pass, Runtime Error.(No such Property "Speciality" for VIP.)
test = new Customer();
test.Name = "test";           // Compile Pass, Runtime Pass.
test.CustomerManager = "mgt"; // Compile Pass, Runtime Error.(No such Property "CustomerManager" for Customer.)
test.Speciality = "";       // Compile Pass, Runtime Error.(No such Property "Speciality" for Customer.)
test = new Parterner();
test.Name = "test";           // Compile Pass, Runtime Pass.
test.CustomerManager = "mgt"; // Compile Pass, Runtime Error.(No such Property "CustomerManager" for Parterner.)
test.Speciality = "";       // Compile Pass, Runtime Pass.


可以看到,对于dynamic关键字声明的对象,是不会在编译时检查类型的成员比如属性的,在runtime时DLR会动态检查对象的成员,并执行之。

有人认为,这样做的好处是,可以在修改了类名的情况下,保持调用者代码不变。我个人认为这个功能意义不大,如果非要保持调用者不变,做好interface就行了,何必这么辛苦呢。更何况,这种静态类型跟动态类型之间的转换,performance还是比较差的,尽管DLR提供了方法调用的cache功能,性能上能有多大提升,还需验证。

第二个方面,是对动态语言的支持。

一个是对IronPython和IronRuby的支持,一个是C#/VB等静态类型语言与动态类型语言的交互。前者超出了C#4.0的范畴,我们重点看看后者。

class MyDynamicObject : DynamicObject
{
Dictionary<String, object> items = new Dictionary<String, object>();
public String Name { get; set; }
public override bool TryGetMember(GetMemberBinder binder, out object result)
{
return items.TryGetValue(binder.Name, out result);
}
public override bool TrySetMember(SetMemberBinder binder, object value)
{
items[binder.Name] = value;
return true;
}
}


如以上代码所示,这个动态类型的核心是TryGetMember和TrySetMember这两个方法(当然,必须继承DynamicObject类,或者DynamiceMetaObject类,或者实现IDynamicMetaObjectProvider接口). 当编译器发现代码试图get/set一个没有预先定义的Property时,就会让DLR去TryGetMember/TrySetMember方法中寻找对应的Property。这个实现也是与动态语言比如IronPython/IronRuby交互的基础。

2. 命名参数和可选参数

这个比较简单,相信下面的代码可以很快让人明白这个特性。

可选参数示例(顾名思义,就是定义过默认值的参数,是可选的,调用方可以决定是否传值进来)

public void Process( string data, bool ignoreWS = false, ArrayList moreData = null )
{
// Actual work done here
}
ArrayList myArrayList = new ArrayList();
Process( "foo" ); // valid
Process( "foo", true ); // valid
Process( "foo", false, myArrayList ); // valid
Process( "foo", myArrayList ); // invalid. 注意这里,下面的命名参数可以解决这个问题。


命名参数示例(对于上面最后一种情况,可以在调用方显示制定传入值属于哪个参数)

ArrayList myArrayList = new ArrayList();
Process( "foo", moreData: myArrayList); // valid, ignoreWS omitted


3. 特定于COM的互操作

还记得这样的代码么, 当我们想访问一个Excel文件, 我们需要写一堆无用的ref missing, 如下:

Excel.Application app = new Excel.ApplicationClass();

Excel.Workbook book= app.Workbooks._Open(@"Sample.xls",
Missing.Value,Missing.Value,Missing.Value,Missing.Value
,Missing.Value,Missing.Value,Missing.Value,Missing.Value
,Missing.Value,Missing.Value,Missing.Value,Missing.Value);


现在, 简化成下面这样了,

Excel.Application app = new Excel.ApplicationClass();

Excel.Workbook book= app.Workbooks._Open(@"Sample.xls");


对此, 没什么好说的, 只能说原来太失败了.

据介绍, 在C#4.0中, 对COM的互操作主要做了一下改进,

Automatic object -> dynamic mapping

Optional and named parameters

Indexed properties

Optional “ref” modifier

Interop type embedding (“No PIA”)

有空可以仔细研究每个改进的细节.

4. 协变和逆变

这个feature更像是修bug。

我觉得需要先解释一下什么是协变, 什么是逆变.

所谓协变, 是指把类型从子类变到基类; 逆变, 则是把类型从基类变到子类. 在C#3.0中, 也有协变和逆变, 是针对delegate做的, 看如下代码:

public class Animal { }
public class Cat : Animal { }
public delegate Animal AniHandler(Animal a);
public static Animal AniMethod(Animal a) { return null; }
public static Cat CatMethod(Object o) { return null; }
public static void TestCovariance()
{
AniHandler handler1 = AniMethod;
AniHandler handler2 = CatMethod;   // CatMethod返回值, 做了协变, CatMehod的返回
// 值Cat, 变到了AniHandler的返回值Animal;
// CatMethod的参数, 做了逆变, CatMethod的参
// 数Object, 变到了AniHandler的参数Animal.
}


在C#4.0中, 增加了对泛型的支持, 包括delegate的泛型参数和泛型的interface. 还是拿delegate举例,

协变, 如下代码在C#4.0以前,是不合法的:

delegate T THandler<T>();

static void Main(string[] args)
{
THandler<Cat> catHandler= () => new Cat();
THandler<Animal> aniHandler = catHandler;   //这段代码会报错.
}


在C#4.0里面, 因为有了对泛型委托协变的支持, 可以稍作修改如下:

delegate T THandler<out T>();                     //此处声明为协变变量

static void Main(string[] args)
{
THandler<Cat> catHandler= () => new Cat();
THandler<Animal> aniHandler = catHandler;   //这段代码就不会报错了.
}


逆变, 如下代码在C#4.0以前, 是不合法的:

delegate void THandler<T>(T t);
public static void TestContravariance()
{
THandler<Animal> aniHandler = (ani) => { };
THandler<Cat> catHandler = aniHandler;    //这段代码会报错.
}


在C#4.0里面, 因为有了对泛型委托逆变的支持, 可以稍作修改如下:

delegate void THandler<in T>(T t);
public static void TestContravariance()
{
THandler<Animal> aniHandler = (ani) => { };
THandler<Cat> catHandler = aniHandler;    //这段代码就不会报错了.
}


同理, 对于泛型interface, 也是如此.

关于协变和逆变, 借鉴了这篇帖子http://www.cnblogs.com/fox23/archive/2010/03/09/1615698.html, 可以去这篇帖子里看更详细的说明.

以上是C#4.0在语言层面的新特性, 当然, 还有很多细节, 光协变和逆变就可以写出一本书, 留待以后研究.
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: