【问题标题】:Polymorphism Not Working Like I Thought It Would?多态性不像我想象的那样工作?
【发布时间】:2010-11-05 16:18:00
【问题描述】:

我有一个处理汽车贴花信息的超类。我有一个处理交易的特定贴花信息的子类。我在事务贴花中有一个特殊的覆盖,用于在设置贴花 # 之前检查某些事情。我遇到的问题是,有时我需要获取有关通用贴花对象的信息并将其设置为我的事务贴花。例如:

TransactionDecal myTransactionDecal = new TransactionDecal();
Decal myGenericDecal = new Decal();
myTransactionDecal = (TransactionDecal) myGenericDecal.getGenericDecal();

但我收到一个运行时错误,告诉我无法在类型之间进行转换。我到底做错了什么,这是正确的方法吗?谢谢!

【问题讨论】:

  • 我认为添加类定义将有助于解决问题。
  • VB.NET 最新版本。错误消息是:无法将“贴花”类型的对象转换为“交易贴花”类型。
  • 你为什么要在贴花上做 getGenericDecal()?不应该只是 myTransactionDecal = (TransactionDecal) myGenericDecal;我同意 CAbott,添加你的类定义。
  • getGnericDecal() 方法只是一个示例,因为我实际上是使用一种方法来获取通用贴花。对不起,应该更清楚。所以我真正拥有的是:myTransactionDecal = GetGenericDecal();
  • 好的,这里有一个更新,我想我可以更好地总结我的问题。我在特定 TransactionDecal 类上的属性具有特定的设置器约束。因此,当我尝试将其转换为基础时,它会起作用,但我失去了设置器约束功能,因为它只使用基础的方法。

标签: inheritance casting polymorphism


【解决方案1】:

可替代性原则表明任何类型都可以被其自身或子类型替代。

在这里,您正在做相反的事情 - 您尝试在子类型的变量中引用超类型的实例(假设 Decal 是超类,而 TransactionDecal 是子类)。

这是被阻止的,因为存在继承是为了专门化现有的类,即扩展接口;一般来说,替换超类会限制接口,这将允许调用未在超类中实现的方法(通过引用变量类型)。

【讨论】:

  • 啊,我明白你在说什么。那么解决方案是什么?我是否应该制作一个只接受超类的值并将它们设置为子类的值的适配器。不过,这似乎不太实用。必须有某种方法来做到这一点。
  • 父对象的属性是否对子类可见,无论是公共的还是通过mutator方法?或者您是否需要从父级读取静态值?我不确定为什么你不能在不需要新对象的情况下从子类访问这些值?另一种可能性是将业务规则卸载到“Transaction”实例中,并赋予它与特定 Decal 实例的“has-a”关系(聚合)。
  • 杰里米,请阅读我对 JP 帖子的评论。我想我已经发现我真的没有必要在这里投射。
  • 谢谢,除了 Jeremy 的解决方案之外的所有解决方案都让我意识到我做错了什么。因为他说强制转换应该只改变变量的值,所以我开始考虑我正在使用的克隆方法。克隆时,我正在创建一个新贴花,将其属性设置为当前实例,然后尝试将其转换为 transactionDecal。那是行不通的,因为我在克隆方法“New Decal()”中说过。相反,我创建了一个将任何类型的贴花作为参数并设置其道具的复制方法。这样我仍然可以使用它的特殊设置器,因为我没有强制转换为基本类型。
【解决方案2】:

您不能这样做,因为您会将不太专业的类 (Decal) 转换为更专业的类 (TransactionDecal)。这是行不通的,因为现在你可以调用 Decal 上没有实现的方法(想想 TransactionDecal 中可能新引入的变量,这些变量被新方法或覆盖的方法使用)它们不可能存在在贴花的一个实例中。

【讨论】:

    【解决方案3】:

    Jeremy 解释为什么这行不通。

    如果你想解决这个问题,你可以让 transactionDecal 不继承自 Decal,而是包含一个 Decal 的实例。这样您就可以将内部实例设置为通用贴花,而不会丢失任何交易数据。

    当然还有其他模式,但这是处理此类问题的更流行的模式之一。

    【讨论】:

      【解决方案4】:

      使用您的示例说明如何实现多态性:

      // cast derived object as base object
      Decal myGenericDecal = null;
      TransactionDecal myTransactionDecal = TransactionDecal.GetTransactionDecal()
      myGenericDecal = myTransactionDecal;
      
      // cast base object to a derived object
      TransactionalDecal newTransactionalDecal = null;
      newTransactionalDecal = (TransactionalDecal)myGenericDecal;
      

      【讨论】:

        【解决方案5】:

        这取决于你想用 myTransactionDecal 做什么,尤其是你是否需要 TransactionDecal 的数据成员。添加一个构造函数怎么样?

        TransactionDecal::TransactionDecal(const Decal &);
        

        或者只是将您想要的功能移到 Decal 中,或移到非成员函数中?

        【讨论】:

          【解决方案6】:

          这称为向下转换。这是一个很好的 quick intro 什么时候可以,什么时候不行(无论如何在 C# 中)......

          Animal a;
          a = b;
          Bird b = (Bird) a; // Okay
          

          此代码将成功编译。 在运行时,强制转换运算符 执行检查以确定是否 所指的对象确实是 输入鸟。如果不是,则运行时 引发了 InvalidCastException。

          但是,您不需要这样做。您的派生类型应该能够做您的基本类型可以做的所有事情。如果你有一个基类型并且想要一些只有派生类型才能做的事情,那么你最好得到一个派生类型的实例来做这件事! :)

          根据评论进行编辑:没有理由不能提供 Derived 构造函数,该构造函数采用 Base 的实例并围绕该实例构建 Derived(例如围绕特定 Base 的默认 Derived)...

          public D(B b)
          {
          }
          

          但是,在我看来,您需要一个接口,或者您应该放宽对方法参数的要求。毕竟,如果您的方法可以很好地对 Animal 进行操作,那么它没有理由强迫您将 Bi​​rd 交给它,这就是您的方法似乎正在做的事情。

          【讨论】:

          • 谢谢。这让我想到了一些事情——我什至需要强制转换的唯一原因是因为我需要基础的实际属性值。我仍然想保留我对子类的所有专门方法调用,所以我真的不认为我应该强制转换:) 克隆方法在这里会更好吗?
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2020-11-14
          • 1970-01-01
          • 1970-01-01
          • 2021-09-10
          • 2016-02-27
          • 2015-05-16
          相关资源
          最近更新 更多