【问题标题】:convert one derived class to another?将一个派生类转换为另一个派生类?
【发布时间】:2014-10-14 17:40:09
【问题描述】:

我有一个类 'timerecord' 和两个类 'stopwatch' 和 'historic',它们继承自此类,但没有使用任何其他属性或方法对其进行扩展。它们的构造函数只是将继承的属性设置为不同的东西。

我希望能够将一种类型的继承类转换为另一种类型(无需手动将所有属性从一种类型复制到另一种类型)

我尝试了所有类型的 ctype 和 directcast 语句,但它们不起作用,我只能转换回继承的类,这不是我想要的。

除了名称之外,这些类都相同。 我希望秒表对象变成历史对象..

有没有办法可以做到这一点?

如果我实现'timerecord'类以及继承?或者将“时间记录”传递给“秒表”和“历史”类构造函数并将其作为属性或其他东西?

一整天都在摸索这个问题,决定注册一个帐户并询问,因为我似乎找不到答案。

Tldr;我想知道如何将一个派生类转换为另一个相同(除了名称之外)的派生类,以及是否有可能?

谢谢

【问题讨论】:

  • 如果它们是相同的,为什么不只是一个TypeCode 属性来区分它们呢?转换将只是更改代码的问题。否则,采用另一种类型的 ctor 重载将允许您从另一种类型创建一个。
  • @Jowijaro :在您的问题中添加代码示例!!!

标签: vb.net class oop inheritance


【解决方案1】:

如果属性相同,您可以将stopwatch 的实例序列化为一个字符串,然后将其反序列化为您的historic 实例。

但是,我认为最好的解决方案是只有一个具有枚举类型属性的 timerecord 类,用于区分 stopwatchhistoric 实例。这样,您可以将 timerecord 类型枚举传递给您的构造函数,并根据枚举值以您需要的方式设置属性。

【讨论】:

    【解决方案2】:

    最好使用枚举并基于它进行初始化。如果唯一的构造函数需要它,则始终必须创建一个或另一个。

    Friend Enum TimeRecordTypes
        Undefined
        StopWatch
        Historic
    End Enum
    
    Public Class TimeRecord
        Public Property Name As String
        Public Property Foo As Integer 
        Public Property Bar As DateTime
    
        Public ReadOnly Property TimeRecordType As TimeRecordTypes = Undefined
        ...
    
        Public Sub New(e As TimeRecordTypes)
            Select Case e
                Case TimeRecordTypes.StopWatch
                   do stopwatch stuff
                Case TimeRecordTypes.Historic
                   do Historic stuff
    
             End Select 
        End Sub
    
        Public Sub ConvertTo(maybe an arg for the type)
           ' convert A to B
        End Sub
    

    【讨论】:

      【解决方案3】:

      (无需手动将所有属性从一个复制到另一个)

      真的吗?
      我认为在某个时候,您将不得不编写更复杂的代码来执行此操作...

      序列化/反序列化...大部分时间都很方便。我使用了这个..直到我在一个类中添加了一个属性,而没有在其他类中添加它们的对应项。像 Point2D 和 X 和 Y 作为双精度类,Vector2D 使用 X 和 Y 作为双精度或 Displacement2D 使用 X 和 Y 作为双精度...直到我在 Vector2D 中添加了属性 Length... 以及以后, Distance 在 Displacement2D.. 中:/

      定义对象类型的属性(枚举)。同样在这里:在某一时刻,为了便于使用,您可以将成员添加到与其他类无关的一个类中。 :/ 这使得你的对象要么无法成长/进化,要么与未使用/假成员一起变得奇怪......

      所以最后,我认为您必须在“冻结”类的当前状态(成员)或编写处理转换所需的每个代码以及在需要时更新这些代码(添加/编辑成员)之间做出选择)


      用 CType 扩展怎么样?

      Public Class StopWatch
          Inherits TimeRecord
      
          ' ...
      
          Public Shared Widening Operator CType(ByVal HistoricInstance As Historic) As StopWatch
              Dim NewStopWatch As StopWatch
              If HistoricInstance IsNot Nothing Then
                  NewStopWatch = New StopWatch(...)
                  ' Set NewStopWatch Properties here...
                  ' ...
                  Return NewStopWatch
              Else
                  Return Nothing
              End If
          End Operator
      End Class
      

      还有:

      Public Class Historic
          Inherits TimeRecord
      
          ' ...
      
          Public Shared Widening Operator CType(ByVal StopWatchInstance As StopWatch) As Historic
              Dim NewHistoric As Historic
              If StopWatchInstance IsNot Nothing Then
                  NewHistoric = New Historic(...)
                  ' Set NewHistoric Properties here...
                  ' ...
                  Return NewHistoric
              Else
                  Return Nothing
              End If
          End Operator
      End Class
      

      注意:如果您将属性添加到 StopWatch 而不是 Historic,则 StopWatch CType 运算符将变为 Narrowing 而不是 Widening。

      当然,每次您将一个类转换为另一个类时,这都会创建该类的一个新实例。有时,当您希望两个类都使用相同的包含对象(而不是新实例)和几个月后,这并不方便,您忘记了您已经编写了扩展运算符以创建所有成员的新实例...

      假设您有一个 StopWatch 类型的 MyStopWatch 类,以及在构造函数中设置为 Date.Now 的属性 InitializationDateTime..

      MyStopWatch = New StopWatch() ' .InitializationDateTime = Date.Now
      ' do some stuff...
      MyHistoric = CType(MyStopWatch, Historic) ' where MyHistoric.InitializationDateTime = Date.Now
      

      使用上面的代码,即使你假设你已经正确地将 MyTimeRecord 转换为 MyHistoric,你也会有不同的属性 InitializationDateTime 值,这是因为 CType 稍后创建了一个 Historic 实例 比 MyTimeRecord 初始化的时间。

      那怎么办?序列化、TypeCode 属性/枚举或 CType 扩展?

      Plutonix 有另一种方式:

      Public Class StopWatch()
          Public Sub New(ByVal HistoricInstance As Historic)
              ' Copy Properties values here...
          End Sub
      End Class
      ' Do the same for Historic Class.
      

      或者你也可以制作函数(VB.net的意思)

      Public Class StopWatch()
          Public Function ToHistoric() As Historic
              Dim NewHistoric As New Historic()
              ' .. set NewHistoric Properties values here
              Return NewHistoric
          End Function
      End Class
      

      但结果是一样的:您必须编写代码来一一设置属性值。好处是:您编写一次代码,最终更新它,但您可以随时使用它,只需一行代码...

      DirectCast 很方便,但容易导致运行时崩溃。
      TryCast 很有趣,但无论如何你都必须检查转换失败。 :/

      还有其他方法可以做到,但是从现在开始,建议的方法之一是否足够有用,或者您有一些特定要求,例如上面的问题?

      【讨论】:

      • 扩大似乎对我有用。由于我无法控制的原因,我正在使用的系统使用了两个相同类型的集合。一个集合存储当前的秒表,另一个存储历史。类型之间的唯一区别是它们所在的集合。为了改变这一点,我想要一个基类型的 keyedCollection,并创建一个我指定添加到集合中的派生类型,而不是单个泛型类型。
      • 当我需要复制从“秒表”到“时间记录”的“历史”定义时,困难就来了。因为之前是通过将秒表集合中的一个项目删除到历史集合中来完成的,所以我认为在我的集合中我可以将一种类型转换为另一种类型。扩大转换将使我能够做到这一点
      • 我现在可以拥有一个“秒表”类型,当它变老时可以转换为“历史”类型,就像在旧系统中一样。但是这种方式我只使用了一个强类型集合,并且可以使用继承来分离类型,它使代码更清晰,更容易理解正在创建的内容,也使我更容易维护,实际上很多比原来的代码少。谢谢。
      • 唯一不好的是,我仍然需要在加宽操作中一次一个地设置新对象的属性。但我想这只是懒惰,能够说 ctype(mystopwatch,history) 并让它在那里发生的好处比每次我都必须创建一个新的历史项目并将值复制到它的属性中要简洁得多需要一个。此外,我确信使用反射将一个属性复制到另一个的循环将很容易。再次感谢
      【解决方案4】:

      我已经尝试过提供的解决方案,并且在这样做时意识到该解决方案不是使用继承,而是因为它们完全相同但名称不同,所以使用接口。 我创建了一个接口和另一个类'basetimerecord',所有三个类都实现了这个接口。 basetimerecord 的属性指向变量。

      其他类属性指向basetimerecord 类型的私有变量的属性。 basetimerecord 通过公共只读属性公开。 这些类中的每一个的构造函数都可以将basetimerecord 作为参数。当他们这样做时,他们会用它覆盖自己的私有变量,因此我可以通过每次只更改一个私有变量来复制所有属性。

      我现在可以做history = new historical(stopwatch.basetimerecord)

      【讨论】:

        最近更新 更多