如何在c#中将接口转换为其类型?

3

我有一个返回接口的属性。在调试过程中,我可以在返回值处打断点,虽然它是接口,但Visual Studio足够聪明,知道实际上它是哪个派生类型。我猜测它是使用反射或类似的东西实现的。我的问题是,在运行时是否可以让我获得相同的信息,以便我可以创建一个适当类型的变量,并将接口强制转换为该类型?这是我的意思:

IPreDisplay preDisplay = cb.PreDisplay;

如果preDisplay是RedPreDisplay,则我想要编写以下代码。
RedPreDisplay tmp = preDisplay as RedPreDisplay;

如果 `preDisplay` 是一个 `GreenPreDisplay` 的话...
GreenPreDisplay tmp = preDisplay as GreenPreDisplay;

我希望能够避免冗长的 switch 语句,如果可以使用泛型那就太好了。
如果您有任何建议或示例,欢迎分享。

9
为什么你想要这么做?当你将接口转换为适当的类型并使用时,接口返回的本意不就失去了吗? - shahkalpesh
2
你的问题表明了一些基本的困惑(或者你在用词方面有点随便)。因为接口在运行时并不存在,所以没有什么可以“返回”一个接口。它返回的是某个具体类型的对象,被“强制转换”成了一个接口。但是这种转换并不会改变对象本身,它只是改变了持有对象引用的变量的性质。 - Charles Bretana
@Charles Bretana:很不幸,您感到有必要陈述刚才所说的话;太多人不理解对象实例和其投射类型之间的区别。 - Randolpho
我建议阅读里氏替换原则:(所有派生类型都应该能够被视为基本类型): http://en.wikipedia.org/wiki/Liskov_substitution_principle - Mark Simpson
5个回答

17
当你遇到需要这样做的情况时,意味着你正在做一些错误的事情。你需要回过头来找出为什么你的设计需要你这样做。如果你被卡住了,我强烈建议你发布一个新问题来获取有关设计的帮助 - 这里有很多聪明的人可以提供帮助。
直接回答你的问题,不,你不能在没有任何if/else或条件语句的情况下这样做,因为你必须使用静态类型进行显式指定。你可以使用反射调用方法,但由于你似乎需要调用接口不支持的方法 - 但某些对象支持 - 你仍然需要编写特定于每种静态类型的条件以调用该方法。直接编码静态类型。
编辑:根据评论中的讨论,解决此问题的最佳方案是向具有此其他属性或方法的类添加第二个接口。然后,您可以进行简单的检查:
IPreDisplay display = cb.PreDisplay;
IOtherInterface displayAsOther = display as IOtherInterface;
if(displayAsOther != null)
{
    displayAsOther.OtherMethod();
}

1
在C# 4.0中,你可以使用dynamic关键字,在运行时将一个类型分派给具有特定签名的方法,而无需使用switch/else。 - LBushkin
@LBushkin非常正确,但C# 4.0尚未发布,假设每个遇到问题的人都可以轻松切换到beta版甚至RTM版是不合理的。 - Rex M
即使在C# 4中可能做到这一点,但这仍然可能不是一个好主意。即使在支持它的语言中,比如VBScript,我也从不使用它。 - Guffa
@RexM - 我知道 C# 4.0 还没有推出,但是让人们了解这种技术还是很值得的,因为它最终会推出。@Guffa - 你为什么认为这种动态分派本身就是一个坏主意?像任何技术一样,它应该只在适当的情况下应用。如果不知道 OP 正在尝试做什么和有什么限制,你不能断言是否有更好的替代方案。 - LBushkin
1
IPreDisplay在整个应用程序中都被使用。一些实现它的对象没有我需要的属性,而这个接口又不能提供。我认为最好的选择是创建一个新接口,并让一小部分对象实现它。也许这就是解决方法。 - Hcabnettek
1
@Kettenbach,这绝对听起来是一个不错的方法。接口应该尽可能地限制在特定目的范围内,因此,如果您有一组新的功能子集需要某些对象实现,那么应该使用单独的接口。 - Rex M

6
使用接口的整个目的在于,执行代码不必了解确切的类型。尽可能通过接口本身公开所需的所有信息,这样就不需要进行强制转换。
可以理解为,在极少数情况下,您可能仍然需要将接口转换为具体实现(特定类型)。如果您能提供更多上下文,那将非常有帮助。

4

根据您要做的事情,您应该在接口中添加一个操作方法/属性,这样您就不需要知道类型 - 这就是多态。

例如:

 IResultLiteEntity preDisplay = cb.PreDisplay;
 preDisplay.Render (); // New Render method on the interface...

3

@Rex M 绝对正确。问题在于你的代码和底层结构。通常情况下,你不应该做你试图做的事情;只针对接口编码。

话虽如此,如果你继承了糟糕的代码并需要进行 monkey-patch,则可以使用 is 运算符。例如:

if(myInstance is MyBaseType)
{
  MyBaseType myInstanceAsBaseType = myInstance as MyBaseType;
  // handle MyBaseType specific issue
}
else if(myInstance is MyOtherBaseType)
{
  MyOtherBaseType myInstanceAsOtherBaseType = myInstance as MyOtherBaseType;
  // handle MyOtherBaseType specific issue.
}

泛型无法帮助您,您也无法在 switch 语句的一部分中完成此操作。但这将使您获得可工作的东西,尽管以非常丑陋的方式工作。


3
正如其他回答者所指出的那样,您可能应该考虑为什么您的设计需要针对不同类型进行不同的逻辑处理,而这些逻辑无法提取到接口中。
然而,假设有充分的理由这样做,您只有几个选择:
1. 使用反射。 这通常是慢且容易出错的代码,而且在实现更改时(即方法重命名等)也很脆弱。 2. 使用 if / else if / else 模式 来根据类型的运行时检查进行调度。这基本上是您在 C# 4.0 版本之前的唯一选择。 3. 如果您正在使用 C# 4.0,则可以将对象分配给动态变量,并在运行时调度到每个支持的类型的签名不同的重载方法(请参见下面的示例)。
以下是一个 C# 4.0 动态调度示例:
void Foo()
{
  dynamic preDisplay = cb.PreDisplay;
  DoSomethingWith( preDisplay );  // runtime dispatch using dynamic runtime (DLR)
}

void DoSomethingWith( RedPreDisplay r ) { ... }  // code specific to RefPreDisplay
void DoSomethingWith( GreenPreDisplay g ) { ... } // code specific to GreenPreDisplay
void DoSomethingWIth( IPreDisplay o ) { ... }  // catch-all

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