【问题标题】:How can I cast an Interface as its type in c#?如何在 c# 中将接口转换为其类型?
【发布时间】:2009-08-27 17:26:44
【问题描述】:

我有一个返回接口的属性。在调试期间,我可以中断返回的内容,虽然它是接口,但 Visual Studio 足够聪明,可以知道它实际上是派生类型。我认为它正在使用反射或其他东西。我不知道。我的问题是,我是否可以在运行时获得相同的信息,以便我可以创建适当类型的变量并将接口转换为该类型?这就是我要说的:

IPreDisplay preDisplay = cb.PreDisplay;

如果 preDisplay 是 RedPreDisplay 我希望能够编码

RedPreDisplay tmp = preDisplay as RedPreDisplay;

或者如果 preDisplay 是 GreenPreDisplay...

GreenPreDisplay tmp = preDisplay as GreenPreDisplay;

等等... 如果可能的话,我想避免使用凌乱的 switch 语句,如果我可以使用泛型那就太好了。

如果您对我如何做到这一点有任何建议或示例,请分享。

【问题讨论】:

  • 您为什么要这样做?当您想使用它时,当您对正确的类型进行强制转换时,返回接口的目的不是丢失了吗?
  • 您的问题揭示了一些根本性的混乱,(或者您在语言上很草率)没有什么可以“返回”接口,因为接口在运行时不存在。它将某种具体类型的对象“CAST”返回到接口。但是强制转换不会改变对象,它只是改变了持有对对象的引用的变量的性质
  • @Charles Bretana:很遗憾你觉得有必要说明你刚刚做了什么;太多人不理解对象实例和它所针对的类型之间的区别。
  • 我建议阅读 Liskov 替换原则:(所有派生类型都应该能够被视为基本类型):en.wikipedia.org/wiki/Liskov_substitution_principle

标签: c# .net interface casting polymorphism


【解决方案1】:

当您遇到需要这样做的情况时,这意味着您做错了什么。您需要备份并弄清楚为什么您的设计要求您这样做。如果您发现自己卡在那里,我强烈建议您发布一个新问题以获取设计方面的帮助 - 这里有很多聪明人可以提供帮助。

要直接回答您的问题,不 - 如果没有某种 if/else 或条件,您无法做到这一点,因为您必须明确使用静态类型。您可以使用反射来调用该方法,但由于您似乎需要调用接口不支持的东西 - 但有些对象支持 - 您无论如何都需要编写每个静态类型条件来调用该方法。只需直接编码类型即可。

编辑:根据 cmets 中的讨论,最好的解决方案是向具有此其他属性或方法的类添加第二个接口。然后你可以做一个简单的检查:

IPreDisplay display = cb.PreDisplay;
IOtherInterface displayAsOther = display as IOtherInterface;
if(displayAsOther != null)
{
    displayAsOther.OtherMethod();
}

【讨论】:

  • 在 C# 4.0 中没有 switch/else 的情况下,您可以使用 dynamic 关键字在运行时将类型分派给具有特定签名的方法。
  • @LBushkin 非常正确,但是 c# 4.0 还没有发布,假设每个有问题 4 可以解决的人都可以简单地切换到 beta 是不合理的。甚至当它是 RTM 时。
  • 即使在 C# 4 中可以做到,它仍然可能不是一个好主意。即使在支持它的语言中,比如 VBScript,我也从不使用它。
  • IPreDisplay 用于整个应用程序。一些实现它的对象没有接口不提供的我需要的属性。我认为我最好的选择是创建一个新的接口并用一小组对象来实现它。也许这就是这里的解决方案。
  • @Kettenbach 这听起来绝对是一个好方法。接口应尽可能限制用途范围,因此,如果您有一些对象实现的新功能子集,那应该是一个单独的接口。
【解决方案2】:

使用接口的全部目的是执行代码不必知道确切的类型。尝试通过接口本身公开您可能需要的所有信息,这样您就无需强制转换。

可以理解,在极少数情况下,您可能仍需要将接口强制转换为具体实现(特定类型)。如果您能提供更多上下文,那可能会有所帮助。

【讨论】:

    【解决方案3】:

    根据您要执行的操作,您可能应该向接口添加一个操作方法/属性,这样您就不需要知道类型——即多态性。

    例如:

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

    【讨论】:

      【解决方案4】:

      @Rex M 是绝对正确的。问题在于您的代码和底层结构。作为一项规则,你不应该做你想做的事;仅针对接口的代码

      也就是说,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 语句的一部分。但它会让你有一些工作,尽管工作方式非常丑陋。

      【讨论】:

        【解决方案5】:

        正如其他响应者所指出的,您可能应该考虑为什么您的设计需要针对不同类型的不同逻辑,而这些逻辑不能被拉入界面。

        但是,假设这样做有充分的理由,您只有几个选择:

        1. 使用反射。这通常是缓慢且容易出错的代码,当你的实现发生变化(即方法被重命名等)时也很脆弱。
        2. 使用 if/else if/else 模式根据类型的运行时检查进行调度。在 C# 4.0 之前的版本中,这几乎是您唯一的选择。
        3. 如果您使用的是 C# 4.0,则可以使用将对象分配给动态 var,并在运行时分派给重载方法,该方法的签名因支持的每种类型而异(参见下面的示例)。李>

        这是一个 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
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2021-01-17
          • 1970-01-01
          • 1970-01-01
          • 2019-06-09
          • 1970-01-01
          • 2018-08-28
          • 2019-09-16
          • 2011-07-31
          相关资源
          最近更新 更多