【问题标题】:Casting between classes that share the same interface在共享相同接口的类之间进行转换
【发布时间】:2011-02-08 13:12:59
【问题描述】:

我有两个接口IHeaderRowIDetailRow

然后我有一个同时实现 RawRow:IHeaderRow, IDetailRow

的对象

然后我需要将其转换为实现 IHeaderRowHeaderRow

但是当我尝试时,它最终会为 null 或给出异常。

我可以将 ObjectRawRow 转换为接口 IHeaderRowIDetailRow

var ObjectIHeaderRow = ObjectRawRow as IHeaderRow;
var ObjectIDetailRow = ObjectRawRow as IDetailRow;

但我不能将 ObjectRawRow 转换为 HeaderRow ,或将 ObjectIHeaderRow 转换为 HeaderRow

它抛出错误无法将源类型“IA”转换为目标类型“A”

我需要将它转换为实际的类HeaderRow

想法?

编辑:

尽管设置一个明确的演员阵容解决了这个问题,但我想我会为那些想知道为什么我要做我所做的事情的人提供一个答案。

简而言之,我正在按顺序处理一个文件。逐行。我将该行读入 RawRow,直到我查看一些值之前,我实际上并不知道它将是什么类型的行。然后我想将它转换为正确的类型。

【问题讨论】:

  • ObjectRawRow 声明的类型是什么?
  • @SLaks,ObjectRawRow 是 RawRow(实现了 IHeaderRow 和 IDetailRow)
  • 一个更好的方法可能是简单地让 RawRow 实例化适当的实例(像 Factory 一样),如果您愿意,可以使用复制构造函数。

标签: c# interface casting


【解决方案1】:

您只能将对象隐式转换为它们继承自或实现的类型 - 因为 RawRow 不是从 HeaderRow 派生的,所以这是不可能的。

根据您的要求,您可以通过编写 explicit conversion operator、创建接受 RawRow 作为其原型的 HeaderRow 构造函数或修改代码以对 IHeaderRow 进行操作来克服此问题。

【讨论】:

  • 转换运算符效果很好。这让我困扰了很多次。在我尝试序列化对象之前,一切都作为接口工作得很好。然后我最终将它的定义从接口更改为类......然后由于这种强制转换限制而无法实际填充它
  • 语言小问题,OP正在尝试进行显式强制转换,需要实现用户定义的隐式转换运算符。跨度>
  • @Steven - 有趣,你为什么这么说?我最初没有指定显式运算符,但后来出于某种原因对其进行了更改 - 我猜这听起来像是转换可能有损或可能引发异常。我想这个问题还不清楚,所以我可以把它改回未指定的。
  • OP 的演员表是明确的,因为他要求(使用 as 关键字)。关于第二点...我不确定为什么我声称他需要一个隐式转换运算符...这是假设他想更改他的代码以进行隐式转换(实际上他没有)。如果他保留他的显式转换,他确实应该像您所说的那样创建一个显式转换运算符。我为疯狂的断言道歉:)
【解决方案2】:

除非类型之间存在继承关系,否则您将无法进行此转换。如果这不可能,那么您能做的最好的事情就是创建一个显式转换运算符,允许您将一种类型转换为另一种类型。

如果您确实创建了显式转换,您应该了解这将比强制转换慢,因为您将调用一个可行的方法,而不是只更改引用类型而不更改任何内存的强制转换在堆上。

考虑这个无法编译的例子:

class Example
{
    static void Main()
    {
        Foo foo = new Foo();
        Bar bar = (Bar)foo;
    }
}

class Foo { }
class Bar { }

由于类型之间没有继承关系,也没有从FooBar 的显式转换,因此无法编译。

但添加显式转换允许它编译:

class Example
{
    static void Main()
    {
        Foo foo = new Foo();
        Bar bar = (Bar)foo;
    }
}

class Foo
{
    public static explicit operator Bar(Foo foo)
    {
        return new Bar();
    }
}
class Bar { }

【讨论】:

  • 呸,对不起,安德鲁,我编辑了你的而不是我的——然后又把你的回滚了。 D:
  • @Jeff - 不用担心!我之前已经做过很多次了:)
【解决方案3】:

您不能将 ObjectRawRow 转换为 HeaderRow,除非一个继承自另一个。

接口与它无关。


考虑:

class Shape
interface IHasCorners
class Rectangle : IHasCorners, Shape
class Triangle : IHasCorners, Shape


Rectangle myRectangle = new Rectangle();
Triangle myTriangle = new Triangle();

//upcasts
Shape s = (Shape)myRectangle;
IHasCorners hc = (IHasCorners)myRectangle;

//downcasts
Rectangle r2 = (Rectangle)s;
r2 = (Rectangle)hc;

//upcasts
s = (Shape)myTriangle;
hc = (IHasCorners) myTriangle;

//these downcasts won't work
//the variables now reference a Triangle instance
Rectangle r3 = (Rectangle)s;
r3 = (Rectangle)hc;

【讨论】:

    【解决方案4】:

    为什么首先需要将其转换为 HeaderRow?如果 IHeaderRow 生成了 HeaderRow 实现的 api,那么您应该能够使用定义的方法对 IHeaderRow “对象”进行操作。

    接口的意义在于,您可以将一组不同的对象视为相似的类型。不是这样您就可以在未通过继承链接的类之间转换不同的对象。

    【讨论】:

      【解决方案5】:

      首先,你为什么需要做这么奇怪的演员表?对于您正在尝试做的事情,可能还有另一种设计。

      其次,您不能进行演员表的原因是 RawRow 不是 HeaderRow。它做出的唯一保证是它实现了IHeaderRow。问题是它还有很多其他的东西,HeaderRow 没有的东西。反之亦然 - HeaderRow 可能有一堆 ObjectRawRow 没有的东西。

      想象你的课程是这样的:

      interface IHeaderRow
      {
          string GetText();
      }
      
      class HeaderRow : IHeaderRow
      {
          public string GetText()
          { 
              return "My Label";
          }
      
          public int GetFoo()
          {
              return 42;
          }
      }
      
      class ObjectRawRow : IHeaderRow
      {
          public string GetText()
          {
              return "My Raw Label";
          }
      }
      

      现在,如果你这样做,你就可以了:

      ObjectRawRow row = new ObjectRawRow();
      IHeaderRow header = row as IHeaderRow;
      string label = header.GetText();    // fine, since GetText is guaranteed to exist
      

      但是试穿一下这个尺寸:

      ObjectRawRow row = new ObjectRawRow();
      HeaderRow header = row as HeaderRow;
      int magic = header.GetFoo();       // BOOM! Method doesn't exist,
      // because the object isn't really a HeaderRow under the covers.
      // It's still really an ObjectRawRow. What do you do now? Crash hard is what.
      

      这就是为什么你不能在继承树之外进行强制转换。

      【讨论】:

      • 您的示例不清楚 - 您能否说明为什么不能对引用 ObjectRawRow 实例的 HeaderRow 类型的变量调用 GetText。
      • 我猜不是你技术上做不到,而是编译器甚至不会让你足够接近尝试。它甚至不允许您将 ObjectRawRow 强制转换为 HeaderRow,因为这样您就为 CLR 崩溃的可能性做好了准备。必须始终维护调用任何和所有 HeaderRow 方法是安全的“合同”。
      • "编译器不会" : HeaderRow hr = (HeaderRow)(object) someObjectRawRow;
      【解决方案6】:

      您可以使用explicit keyword 创建在您尝试从IA 转换为A 时调用的方法。如果不编写自己的方法,它就无法工作的原因是编译器不知道如何处理未提供的值。

      【讨论】:

      • 转换操作符在编译时被调用。他们可能在这里帮不上忙。
      【解决方案7】:

      只有当对象实际上是该类的实例(或派生自该类)时,才能将实例强制转换为特定类。

      即使它们实现了相同的接口,也不可能将 A 类的实例转换为完全不相关的 B 类(这是您尝试做的)。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2018-10-31
        • 1970-01-01
        • 2023-03-19
        • 2012-02-25
        • 1970-01-01
        • 2015-01-01
        • 1970-01-01
        • 2015-09-01
        相关资源
        最近更新 更多