接口继承和类型转换

4

我创建了一个接口,它继承自另一个(COM)接口:

public interface IDTSComponentMetaData : IDTSComponentMetaData90 { }

这就是它的全部功能。

原因在于,根据我使用的SQL Server版本不同,基础可能是IDTSComponentMetaData90(2005)或IDTSComponentMetaData100(2008)。为了避免在代码中有条件地编译对IDTSComponentMetaData90 / IDTSComponentMetaData100的每个引用,我想使用版本中立的接口来简单地包装适当的真实接口。

问题在于,SSIS在一个关键点向我传递了一个本地接口的对象,而我需要将其转换为我的包装接口:

#if SQL2005
public void Initialize(IDTSComponentMetaData90 c,IServiceProvider s) {
#elif SQL2008
public void Initialize(IDTSComopnentMetaData100 c,IServiceProvider s) {
#endif
  m_ComponentMetaData = (IDTSComponentMetaData) c;
  m_ServiceProvider = s;
}

这段代码可以编译通过,但在运行时我遇到了一个“无法将类型为'System.__ComObject'的COM对象转换为接口类型'MyNameSpace.IDTSComponentMetaData'”的错误。这是因为对IID为'{483E01E7-001C-35E4-Ac9f-4B0C1B81E409}'的接口进行COM组件的QueryInterface调用失败,导致出现“没有支持此类接口”的错误(HRESULT: 0x80004002 (E_NOINTERFACE))。

我的做法完全错误吗?

4个回答

5

你为什么不根据SQL Server版本简单地创建接口别名呢?

#if SQL2005
using IDTSComponentMetaData = IDTSComponentMetaData90;
#elif SQL2008
using IDTSComponentMetaData = IDTSComopnentMetaData100;
#endif

因为我是一名真正的C++程序员,从未知道using可以以那种方式使用。我现在要尝试一下,如果它能够工作,你就帮我省去了很多痛苦! - Marc Bernier
那个方法很好,除了你不能在中央指定它,你必须在每个文件的顶部指定你想要使用的接口别名。 - Lasse V. Karlsen
这看起来像是在工作!太糟糕了,C#没有#include指令,因为Lasse关于重复的说法是正确的,而且有大约14个.cs文件需要进行20多次替换。但这仍然比另一种选择要好得多,那就是维护两套源代码,一套用于2005年,另一套用于2008年。我想CRTL+C将成为我接下来几个小时最好的朋友... - Marc Bernier

1

很遗憾,你试图做的事情行不通。

问题在于底层对象实际上没有实现你的接口。它只实现了特定版本的接口,也就是你试图摆脱的那个。

因此,当你将你的对象转换为该接口时,它会失败。

C#中最好的方法是通过给接口取别名来解决问题,但不幸的是,这必须在每个需要使用该接口的 .cs 文件的顶部进行。

有没有可能创建一个包装器对象来代替实现你的接口?然后你自己的对象将实现新的接口,并将每个调用传递回底层对象的相关方法/属性。在那个对象中,你可以做各种 #if 和类似的处理来处理差异,但将差异隐藏在程序的其余部分之外。

或者,你可以创建两个类,都实现你的新接口,并使用IoC容器或类似的东西来请求“实现这个接口的对象”,而不知道你实际上正在与哪一个交流。


我本来也想这么做,但是@dtb的解决方案实际上更容易实现,因为我需要编写大约14个包装类。 - Marc Bernier
他的方法有效吗?如果有效,那将为我节省大量类似的问题,请告诉我们。 - Lasse V. Karlsen
是的,非常好。我不得不在一堆.cs文件的顶部添加了大约30行“using”代码,但我编译并运行了两个版本,没有遇到任何问题。 - Marc Bernier

0

我相信COM接口的名称并不重要,因此创建自己的COM接口也可能有效(尽管我无法测试)。

#if SQL2005

[ComImport]
[Guid("XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IDTSComponentMetaData
{
    // all members of IDTSComponentMetaData90
}

#elif SQL2008

[ComImport]
[Guid("YYYYYYYY-YYYY-YYYY-YYYY-YYYYYYYYYYYY")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IDTSComponentMetaData
{
    // all members of IDTSComponentMetaData100
}

#endif

0

COM接口名称映射到接口ID(IID); 这个错误告诉你,你正在使用的对象不支持IDTSComponentMetaData90。

我认为你最好的选择是明确定义IDTSComponentMetaData,而不是从IDTSComponentMetaData90派生它。然后,您可以创建两个不同的类,通过转发方法调用到适合SQL Server版本的COM接口来实现接口。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接