第28章 增强的COM Interop

以.NET为代表的托管代码世界,和以COM(Component Object Model,组件对象模型)等技术为代表的非托管代码世界,它们的编程模型完全不同。为了保持向下兼容,.NET通过引入COM Interop(COM互操作)作为连接两个世界的桥梁。COM Interop可以使得.NET程序在不修改原有COM组件的前提下方便地访问COM组件,这一点是非常重要的。事实上,对于已存在的大量COM组件,如非因为重构或者添加新的功能,而仅仅因为要改写成.NET代码,则没有资源也没有可能完全重写它们。所以COM Interop的引入为有此需求的开发者提供了很好的解决方案。COM Interop就是设计用来和现存的COM组件实现互操作的。

微软在.NET运行时中添加了新的命名空间:System.Runtime.InteropServices,以支持COM Interop,要注意的是,这种互操作是双向的,如图28-1所示。

第28章 增强的COM Interop - 图1

图 28-1 COM Interop图示

❑.NET代码可以调用COM组件;

❑COM组件可以调用.NET组件。

C#4.0通过新增的一些特性简化了COM Interop调用,这些新特性包括:

❑可选和命名参数

❑Dynamic lookup

❑类型内嵌

另外,C#4.0还改进了参数传递机制,都大大简化了对于COM组件的调用。

28.1 Dynamic lookup

Dynamic lookup(动态查找)是C#4.0引入的一个新的动态机制,可以用统一的方式来动态调用成员。这种动态机制是构建于Dynamic Language Runtime(动态语言运行时,DLR)之上的,DLR是CLR的扩展。有了动态查找,当你拿到一个对象时,不需要关心它是来自于COM还是Python、Ruby[1]、Javascript、HTML DOM或反射,只需要对其进行操作即可,剩下的工作由运行时去做,它会指出针对特定的对象时这些操作的具体意义。

显然,这带来了巨大的灵活性,并能极大程度地精简代码,但它伴随着一个巨大的缺点——不会再为这些操作维护静态类型。在编译时,会假设动态对象支持任何操作,而如果它不支持某个操作,则只有到运行时才能得到错误,如图28-2所示。有的时候这不会有任何问题,因为对象根本不具有静态类型,而且在其他情况下必须在简洁和安全之间进行权衡。为了帮助进行这种权衡,C#的一个设计目标就是允许在每个单独的调用中选择是否使用动态行为。

第28章 增强的COM Interop - 图2

图 28-2 对于动态类型的类型检查延迟到了运行时

但是,要指出的是,C#4.0引入这种动态机制的动机,并非打算把C#打造成一种新型的具备动态类型的语言,而是在需要和动态语言打交道的时候,这种交互可以更加便捷。

动态查找的应用场景主要包括:

❑办公自动化。其实就是在调用Office的COM Interop时更加方便,避免了不必要的类型转换,稍后会介绍这部分内容并进行相应对比;

❑与动态语言交互。例如,与包括Python和Ruby在内的动态语言交互,在Silverlight中与JavaScript的交互等;

❑调用反射。更方便的运行时动态调用。

28.1.1 dynamic关键字

在C#中,动态查找是通过新增的dynamic关键字体现的。

在C#4.0之前,如果我们拿到一个对象,但是不知道该对象具体的类型(类或接口),将不得不使用object类型的变量来存储它,更糟的是,如果需要调用这个对象的方法,我们将不得不使用反射,代码繁杂而丑陋,如代码清单28-1所示。

代码清单28-1 使用反射调用未知类型的方法


object someclass=someObject.GetSomeClass();

object result=someclass.GetType().InvokeMember(

"Add",

BindingFlags.InvokeMethod,

null,

new object[]{1,1}

);

int sum=Convert.ToInt32(result);


在C#4.0中,可以使用dynamic关键字来改变这种状况,把之前使用的object类型的变量代之为dynamic类型,接下来只需向该动态对象发出动态调用即可,改进后的代码如代码清单28-2所示。

代码清单28-2 调用动态对象的方法


dynamic someclass=someObject.GetSomeClass();

int sum=someclass.Add(1,1);


可见,代码简化了许多,看起来也非常清爽。但是,在享受动态特性带来的好处的同时,也要注意如下几个问题:

❑在使用动态对象的时候,因为编译期的静态类型检查不再实施,这意味着在编译期能捕获的错误将更少;

❑Visual Studio的IntelliSense对于动态对象将不再有效,因为类型的具体信息只有在运行时才能得到;

❑动态对象在性能测试中的表现也无法和静态类型对象相提并论。

[1]这里指的是IronPython和IronRuby,是Python和Ruby语言的.NET版本,它们都建立于DLR之上。