继承、聚合与包容
更新:2007 年 11 月
COM 在 .NET Framework 中的重复使用可通过继承来实现。COM 类型可以作为基类参与继承。在以下情况下应使用继承、聚合或包容模型:
模型 |
用途 |
---|---|
继承 |
将托管对象当作外部对象公开。 |
聚合 |
使外部对象能够在不进行修改的情况下公开另一个对象的接口实现。 |
包容 |
使外部对象能够修改内部对象的行为。 |
继承
当托管接口向 COM 公开时,它们始终会扩展 IUnknown 或 IDispatch,即使该接口是从托管端的另一个接口继承也是如此。同样的规则也适用于为托管类生成的类接口。
.NET Framework 通过添加实现继承来扩展 COM 模型。托管类型可以直接或间接地从 COM coclass 导出;具体地说,从运行库生成的运行库可调用包装导出。除了在托管代码中实现的方法和属性之外,导出的类型还可以公开 COM 对象的所有方法和属性。所得对象将部分在托管代码中实现,部分在非托管代码中实现。
要符合用作基类的要求,coclass 必须:
可创建
聚合(就 COM 而言)
托管类型可以为合格的 coclass 扩展 RCW 并重写基对象所提供的方法。如果要重写任何方法,则必须重写接口的所有基方法。
托管类型按照继承托管基对象的相同方式来继承 RCW。在下面的代码示例中,托管的 Catapult 类从 AcmeLib.Slingshot 这一 COM 类型导出。
#using "AcmeLib.dll" // Provides definition of Slingshot.
__gc class Catapult : public AcmeLib.Slingshot // Extends the COM type.
{
// Delegates to base implementation.
Load() { //… };
Fire()
{
// Engages in some behavior before delegating to the base
// implementation.
Slingshot::Fire();
}
// The Aim method needs to be overridden.
Aim() { //… }
}
Catapult *cp = new Catapult();
// Calls derived implementation.
cp->Load();
// Calls base implementation.
cp->Aim();
// Calls derived which delegates to base.
cp->Fire();
聚合
要公开一个 COM 类的接口,并将这些接口当作是在第二个 COM 类上实现的,第二个类就将聚合第一个类。COM 对象可以聚合 .NET 对象,在这种情况下,该对象的所有接口(包括其类接口)都可以通过外部对象来使用。内部 .NET 对象将对其 IUnknown 方法的调用委托给控制 IUnknown。
聚合比包容(在下一节说明)略为复杂。它通常用于使外部对象能够在不进行修改的情况下公开另一个对象的接口实现。所有托管对象自动支持将托管对象用作内部对象来进行 COM 样式的聚合。要聚合托管对象,非托管的外部对象将创建托管的内部对象,方法是调用 CoCreateInstance,然后将外部对象的 IUnknown 作为 OuterUnknown 参数传递。当外部 IUnknown 在构造过程中传递给托管对象时,托管对象将缓存接口并以如下方式使用该接口:
外部对象挂接到内部 IUnknown 的非委托 IUnknown。非委托 IUnknown 的运行方式与常规 IUnknown 相同;也就是说,如果对象实现了接口,非委托 IUnknown 就会成功,否则就会失败。非委托 IUnknown 不会将调用转发到外部对象。
如果对内部对象查询它不支持的接口,内部对象就会将调用委托给外部对象的 IUnknown 接口。
对内部对象的 QueryInterface、AddRef 和 Release 方法的所有调用都将委托给外部对象的 IUnknown。
通过这三种行为,可以聚合任何托管对象。利用这种聚合关系,可以使单个 COM 对象部分在托管代码(内部部分)中实现,部分在非托管代码(外部部分)中实现。
包容
.NET 对象可以包含 COM 对象,方法是将其元数据导入 .NET 程序集,然后在另一个类中声明该类型的数据成员。与常规的 COM 包容相同,您可以在您自己的接口实现中调用 COM 对象的接口,但被包含的对象不会在类之外公开。包容比聚合简单。当外部对象需要修改内部对象的行为时,通常会使用包容。要实现此目的,外部对象只需在其构造函数中创建内部对象的一个实例,并在必要时将调用委托给内部对象。外部对象可以选择要委托的调用和要直接处理的调用。在支持包容方面,运行库对于对象没有特殊的要求。
COM 对象也可以包含 .NET 对象。COM 对象的客户端的相关行为与被包含对象是其他任何 COM 对象时完全相同。