原文:http://www.gispower.org/article/arcgis/ao/2008/727/08727194926C5JHDD0AAE64CJCF6632_3.html
这里的内容大部分来自MSDN Library for Visual Studio 2005。因为ArcObjects是采用C++编写的,要在.NET上开发,其所有的组件都是以COM的形式存在。.NET上的所有东西都是托管的,而 COM是非托管的,COM和C#之间的交互是通过包装器来实现的,有两种:RCW(runtime callable wrapper )运行库可调用包装和CCW(com collable wrapper)COM可调用包装。如图所示。下面先说说COM对象和.NET对象的差异,这很重要。
COM 在以下几个重要方面与 .NET Framework 对象模型存在差异:1、COM 对象的客户端必须管理这些对象的生存期;公共语言运行库管理其环境中各对象的生存期。
2、COM 对象的客户端通过对提供服务的接口发出请求并取回接口指针来发现服务是否可用。.NET 对象的客户端可以使用反射来获取对象功能的说明。.
3、NET 对象驻留在由 .NET Framework 执行环境管理的内存中。为了提高性能,执行环境可以将对象在内存中来回移动,并更新对所移动对象的任何引用。非托管客户端在获取指向对象的指针后,将依赖 于该对象来保持其位置不变。这些客户端没有相应的机制来处理位置不固定的对象。
为了克服这些差异,运行库提供了包装类,使托管和非托管客户端认为它们是在其各自的环境中调用对象。每当托管客户端对某个 COM 对象调用方法时,运行库就会创建一个RCW。RCW 的功能之一是抽取托管和非托管引用机制之间的差异。运行库还会创建一个CCW来逆转此过程,使 COM 客户端能够对 .NET 对象无缝地调用方法。如下图所示,调用代码的性质将确定运行库所创建的包装类。
COM 包装概览
大多数情况下,运行库所生成的标准 RCW 或 CCW 将为跨越 COM 和 .NET Framework 之间边界的调用提供充分的封送处理。利用自定义属性,您可以选择性地调整运行库表示托管和非托管代码的方式。对于更详细的有关托管和非托管之间通信的细 节,即这个主题:平台调用和 COM Interop 模型,可参看MSDN Library for Visual Studio 2005中的相关内容。
接着主要讲讲在使用C#进行ArcObjects编程时,在创建对象或者进行类型转换时,为什么总是偏向于使用接口类型,而不是类来声明新对象。
When you create a new COM object in .NET via interop, you get a reference to your object that is wrapped in a strongly typed runtime callable wrapper (RCW). A RCW is a wrapper that can hold a reference to a COM object inside a .NET application.这段话表述的意思很简单:在.NET中创建的所有COM对象都是通过RCW来完成而且只会为每一个COM对象创建一个包装器,看 代码:
ESRI.ArcGIS.Display.ISimpleMarkerSymbol sym = new ESRI.ArcGIS.Display.SimpleMarkerSymbolClass();
Debug.WriteLine(sym.GetType().FullName);
结果如你所料:as you might expect; the variable holds a reference to the ISimpleMarkerSymbol interface of the SimpleMarkerSymbolClass RCW.
但是下面这种情况就不一样了,虽然看起来差不多:
ESRI.ArcGIS.Display.ISimpleMarkerSymbol sym = rend.Symbol as ESRI.ArcGIS.Display.ISimpleMarkerSymbol;
Debug.WriteLine(sym.GetType().FullName);
结果显示,你会觉得很奇怪,sym的类型是:System.__ComObject
这到底是什么类型呢,其实这是可以用来表示任何COM对象的包装器类:This is a class internal to the .NET Framework that can be used to hold a reference to any kind of COM object; its purpose is to act as the RCW for an unknown Type of COM object.有一点要注意的就是,这个通用的COM包装器,其所包含的元数据是很有限的。如果要创建某一确定的类实例,你必须确保可以获得类的元数据, 否则创建ArcObjects对象时总会遇到莫名其妙的错误。特别是关键字“as”进行类型转换时,更容易遇到这个错误。
比如:ESRI.ArcGIS.Display.SimpleMarkerSymbolClass sym2 = sym as ESRI.ArcGIS.Display.SimpleMarkerSymbolClass;//出错
ESRI.ArcGIS.Display.ISimpleMarkerSymbol sym3 = sym as ESRI.ArcGIS.Display.ISimpleMarkerSymbol;//没错
原因还是从System._ComObject身上找,sym变量可能之前就是通过System._ComObject获得对某COM对象的引用,所以没有足够的元数信息被转化成其他强类型包装器。
下面的话就说得很明白了:However, as the System.__ComObject class is specifically designed to work with COM objects, it is always able to perform a QI to any COM interfaces that are implemented by an object. Therefore, casting to specific interfaces (as long as they are implemented on the object) will be successful.