【问题标题】:.NET: Inheritance Modify Base Class (C++ Conversion).NET:继承修改基类(C++ 转换)
【发布时间】:2010-09-04 22:19:48
【问题描述】:

我有两节课。基类是A。继承类是B。我想将一个对象的基类复制到另一个对象的基类中,而不影响原始类。但是,.NET 似乎忽略了复制。这在.NET 中是不可能的吗?我知道这在 C++ 中是可能的。我已经包含了 C++ 代码来说明我想要实现的目标。

我明白,在这个特定的示例中,我可以直接分配值 bClass.ValueA = aClass.ValueA。但是如果 A 类有私人成员呢?这是不可能的。

示例类

Public Class A
    Public ValueA As String

End Class

Public Class B
    Inherits A
    Public ValueB As String

End Class

守则

    Dim aClass As New A
    Dim bClass As New B

    aClass.ValueA = "AClass"

    bClass.ValueA = "BClass"
    bClass.ValueB = "BClass"

    Dim baseBClass As A
    baseBClass = CType(bClass, A)
    baseBClass = aClass

结果:

aClass.ValueA = "AClass"

bClass.ValueA = "BClass"

bClass.ValueB = "BClass"

baseBClass.ValueA = "AClass"

预期结果:

aClass.ValueA = "AClass"

bClass.ValueA = "AClass"

bClass.ValueB = "BClass"

baseBClass.ValueA = "AClass"

C++解释比较

#include <string>
#include <iostream>

using namespace std;

class A {
public:
    string ValueA;
};

class B : public A {
public:
    string ValueB;
};



int main(int argc, char *argv[]) {
    A aClass;
    B bClass;
    aClass.ValueA = "AClass";

    bClass.ValueA = "BClass";
    bClass.ValueB = "Blcass";

    cout << aClass.ValueA <<endl;
    cout << bClass.ValueA << endl;
    cout << bClass.ValueB << endl;

    A *baseBClass;
    baseBClass = (A*) &bClass;
    *baseBClass = aClass;

    cout << aClass.ValueA <<endl;
    cout << bClass.ValueA << endl;
    cout << bClass.ValueB << endl;
    cout << baseBClass->ValueA << endl;


    return 0;
}

预期和实际结果:

aClass.ValueA = "AClass"

bClass.ValueA = "AClass"

bClass.ValueB = "BClass"

baseBClass->ValueA = "AClass"

我认为没有指针这是不可能的。我试过了

Ctype(bClass, A) = aClass
Directcast(bClass, A) = aClass

我收到此错误:

表达式是一个值,因此 不能作为任务的目标。

【问题讨论】:

  • 我不明白你为什么要bClass.ValueA = "AClass"。你确实设置了bClass.ValueA = "BClass"
  • 但是他设置了baseBClass = aClass,我想他打算将aClass.ValueA的值复制到bClass.ValueA中

标签: .net c++ inheritance


【解决方案1】:
Dim aClass As New A
Dim bClass As New B

aClass.ValueA = "AClass"

bClass.ValueA = "BClass"
bClass.ValueB = "BClass"

好的,现在您在堆上分配了两个实例,以及两个引用它们的变量。像这样的:

Variable       Instances on the heap
--------       ---------------------

aClass ------> Instance of class A

bClass ------> Instance of class B

(顺便说一句,您的命名令人困惑。aClass 和 bClass 根本不是类;它们是引用 instances 的变量。)

然后你这样做:

Dim baseBClass As A
baseBClass = CType(bClass, A)

所以现在你有了三个变量,但两个实例和以前一样。其中两个变量(bClass 和 baseBClass)都引用同一个实例:

Variable          Instances on the heap
--------          ---------------------

aClass ---------> Instance of class A

baseBClass -\
             >--> Instance of class B
bClass -----/

baseBClass 变量的类型与 bClass 变量不同,但它们都引用同一个实例。

现在你这样做:

baseBClass = aClass

它将 baseBClass 变量中的引用替换为不同的引用,特别是对上面标记为“A 类实例”的事物的引用:

Variable          Instances on the heap
--------          ---------------------

aClass -----\
             >--> Instance of class A
baseBClass -/

bClass ---------> Instance of class B

您只是在分配参考。您从不实际复制任何数据。

但是根据我收集到的您的描述,您希望其中一些赋值将引用从一个变量复制到另一个变量,而其他赋值将对象的内容从一个实例复制到另一个实例?您使用相同的赋值运算符来表示两件事。您如何期望编译器知道哪个是哪个?

【讨论】:

    【解决方案2】:

    如果我对您的理解正确,那么您试图做的是从一个对象实例中提取部分状态,然后将其塞进另一个实例的喉咙。即,您想违反封装。

    有一些方法可以实现这一点,并且不违反封装,但老实说,我认为这听起来像是一个非常糟糕的设计——C++ 愿意让你侥幸逃脱,但这是一个糟糕的设计尽管如此。听起来您正在尝试滥用继承,而您确实应该使用聚合。你说“B的实例是一个 A”,但实际上并不合适。相反,它应该是“B 的一个实例有一个 A”。或者用代码来表达:

    Public Class A
        Public ValueA As String
    End Class
    
    Public Class B
        Public A As A
        Public ValueB As String
    End Class
    

    注意 B 不再是 A 的后裔;相反,它有一个对 A 实例的引用。当你创建一个 B 时,你需要一些方法来给它一个 A 的实例,所以你可以初始化那个字段——也许 B 的构造函数可以创建一个 A 并将引用分配给该字段,或者您将 A 的实例传递给 B 的构造函数。稍后,如果您确定它引用了错误的 A 实例,您可以将其更改为引用不同的实例。

    【讨论】:

    • 正如讨论的那样,Hydro Composition 是一个不错的选择,但如果这是第 3 方封闭库,则不可行。
    【解决方案3】:

    为方便起见,我只是对您的代码行进行了编号:

    1 Dim aClass As New A
    2 Dim bClass As New B
    
    3 aClass.ValueA = "AClass"
    
    4 bClass.ValueA = "BClass"
    5 bClass.ValueB = "BClass"
    
    6 Dim baseBClass As A
    7 baseBClass = CType(bClass, A)
    8 baseBClass = aClass
    

    您在第 4 行为 bClass.ValueA 分配了值“BClass”,这就是您的输出的原因。

    【讨论】:

    • 谢谢 Jeroen,我已经解释了更多我的意思。
    • 我认为因为在第 8 行,baseBClass 现在应该是一个 A 类,当他进行分配时,baseBClass 应该具有 aClass 的复制值。
    • 直接赋值没问题,但如果基类有私有成员怎么办。
    • @Shiftbit:你期望 bClass 在哪一行得到你期望它得到的值?
    • 为了实现你确实需要指针,现在你只使用引用。在 C# 中,您可以使用 unsafe 关键字来处理指针。在 VB 中不可用。
    【解决方案4】:

    当您分配 baseBClass = aClass 时,您正在分配一个指向 aClass 的指针。

    这将替换存储在此处的指向 bClass 的指针:baseBClass = CType(bClass, A)。

    我认为你可能需要在 A 中使用复制构造函数才能达到预期的效果。

    【讨论】:

    • 复制构造函数或组合继承会有所帮助,但如果这些类来自封闭库,则不会。
    • Eek.. Hrm 好吧,让我们等到 .NET 大师发现这一点。我会稍微提一下这个问题,因为我也有兴趣看到结果。
    【解决方案5】:

    另一种方法是使用 C# 类来执行指针操作并向 B 类添加扩展方法。它与 C++ 解决方案非常相似。这似乎是最好的解决方案,因为它几乎与 C++ 实现相同。

    相反,我使用了反射。它只是一个浅拷贝,但在这种情况下它并不重要。 C++ 默认的复制构造函数只是一个浅克隆。通过一些修饰,您可以检查 IClonable 是否存在并尝试克隆它,否则实例化一个新对象。我已对此进行了更新以包括私人成员。


    Imports System.Runtime.InteropServices
    Imports System.Reflection
    
    Public Class A
        Private ValueA As String
        Public Sub New(ByVal ValueA As String)
            Me.ValueA = ValueA
        End Sub
        Public Overrides Function ToString() As String
            Return String.Format("{0}:{1}={2}", _
            Me.GetType.Name, "ValueA", ValueA)
        End Function
    End Class
    
    Public Class B
        Inherits A
        Private ValueB As String
        Public Sub New(ByVal ValueA As String, ByVal ValueB As String)
            MyBase.New(ValueA)
            Me.ValueB = ValueB
        End Sub
        Public Overrides Function ToString() As String
            Return String.Format("{0}:{1}={2},{3}", _
            Me.GetType.Name, "ValueB", ValueB, MyBase.ToString)
        End Function
    End Class
    
    Module Inheritence
    
        Sub Main()
            Dim aClass As New A("AClass")
            Dim bClass As New B("BClass", "BClass")
    
            Console.WriteLine(aClass)
            Console.WriteLine(bClass)
    
            For Each item As FieldInfo In aClass.GetType.GetFields( _
                                                 BindingFlags.Public _
                                                 Or BindingFlags.Instance _
                                                 Or BindingFlags.NonPublic _
                                                 Or BindingFlags.Static)
                item.SetValue(bClass, item.GetValue(aClass))
            Next
            Console.WriteLine(aClass)
            Console.WriteLine(bClass)
            Console.ReadKey()
        End Sub
    
    End Module
    

    基类克隆

    这是我的解决方案所依据的网站。它是一个浅层克隆,但实施起来简单快捷 http://www.codeproject.com/KB/cs/cloneimpl_class.aspx

    此解决方案更好,因为它尝试对支持 IClonable 的对象进行深度克隆,否则它只会设置一个新实例。 http://whizzodev.blogspot.com/2008/03/object-cloning-using-il-in-c.html

    克隆例程:

    这是一个有趣的网站,它解释了各种克隆技术。 http://developerscon.blogspot.com/2008/06/c-object-clone-wars.html

    另一个有趣的资源。 Deep cloning objects

    关于深度克隆的讨论 Create a Deep Copy in C#

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-02-01
      • 1970-01-01
      • 2011-08-27
      • 1970-01-01
      相关资源
      最近更新 更多