C#继承。从基类派生的派生类

26

我有一个基类

public class A   
{
    public string s1;
    public string s2;
}

我也有一个派生类:

public class B : A
{
    public string s3;
}
假设我的程序创建了类A的一个实例。
A aClassInstance = new A();

一些参数已被设置:

aClassInstance.s1 = "string 1";
aClassInstance.s2 = "string 2";

现在我想创建一个类B的实例。但是我希望B已经拥有了我类A的实例的值。

以下方法没有成功:

public B bClassInstance = new B():
bClassInstance = (B)aClassInstance;

这也不行:

在A类内创建一个克隆方法。

public B cloneA() {    
    A a = new A();
    a = (A)this.MemberwiseClone()
    return(B)a;
}

VS Code可以同时接受以上两种情况,但我在运行时出现了错误

请帮忙


2
在克隆时要小心 - 特别是如果您的类具有可变引用类型的字段。决定您想要深度克隆还是浅克隆,并记录它。 - TrueWill
1
这个特定的类没有引用,因此浅克隆适用于它。我在这里找到了一篇关于浅克隆和深克隆的好文章,供有兴趣的人参考:http://itpksingh.blogspot.com/2009/08/shallow-copyingdeep-copyingobject.html - Sam
使用ValueInjector找到了一个解决方案。StackOverFlow目前不允许我“回答自己的问题”。一旦可以,我会发布完整的细节。 - Sam
这个回答解决了你的问题吗?是否可以使用显式类型转换将基类对象分配给派生类引用? - Michael Freidgeim
4个回答

36
你面临的基本问题是需要构造一个类型为B的实例(其中包含类型A的属性)。你复制A实例的方法行不通,因为这会给你一个A类型的实例,你无法将其转换为B
我建议为A类和B类编写构造函数,这些构造函数接受类型为A的参数。B类的构造函数只是将值传递给其基类A。A类的构造函数知道如何将字段复制到自己:
class A {
    public A(A copyMe) {
        s1 = copyMe.s1;
        ...
    }

class B : A {

    public B(A aInstance) : base(aInstance) {
    }

}

使用方法如下:

A a = new A();
a.s1 = "...";

B b = new B(a);

编辑

当你不想在添加新字段或属性时更改A的构造函数时,可以使用反射来复制属性。可以使用自定义属性来装饰要复制的内容,或者只复制A的所有属性/字段:

public A (A copyMe) {
    Type t = copyMe.GetType();
    foreach (FieldInfo fieldInf in t.GetFields())
    {
        fieldInf.SetValue(this, fieldInf.GetValue(copyMe));
    }
    foreach (PropertyInfo propInf in t.GetProperties())
    {
        propInf.SetValue(this, propInf.GetValue(copyMe));
    }
}

我没有尝试过这段代码,但是它的意思应该很清楚。


我正在尝试升级这个程序,以便不必逐个成员进行复制:class A { public A(A copyMe) { s1 = copyMe.s1;(A)copyMe.MemberwiseClone()应该返回我所需的A类的浅表克隆。现在我们只需要将其传回B构造函数...但问题是如何传回。不幸的是我们不能这样做:Class A{ public A(A copyMe){this = (A)copyMe.MemberwiseClone() } } ... } - Sam
1
@Sam:问题在于,你必须构造一个类型为B的对象。因为A是B的一部分,所以你必须找到一种设置新B实例属性(属于类型A)的方法。你的克隆A方法行不通,因为它只会给你一个A类型的实例,而你需要的是B类型的实例!你可以在A的构造函数中使用反射。我会更新我的答案来反映这一点。 - Jan
反射方法会在属性没有setter时抛出异常。检查propInf.CanWrite可以解决这个问题。 - Bill Tarbell

10
你可以在 A 类中创建一个通用的克隆方法:
     public T Clone<T>() where T : A, new() {
          return new T() { a = this.a, b = this.b};
     }

如果您希望使克隆功能可扩展:
     public T Clone<T>() where T : A, new() {
          var result = new T();
          this.CopyTo(result);
          return result;
     }

     protected virtual void CopyTo(A other) {
          other.a = this.a;
          other.b = this.b;
     }

你可以这样使用它:

     A  a = new A();
     // do stuff with a
     // Create a B based on A:
     B b = a.Clone<B>();

请注意:在您的示例中,new A()和MemberwiseClone都将创建类型A的新对象。
如果您不想自己编写复制方法,可以查看类似于AutoMapper的工具。

在这个例子中,我是否需要设置所有成员变量(例如a = this.a,b = this.b等)。如果类有多个成员变量,有没有办法避免编写每一个成员变量的代码?(如果我决定添加另一个成员变量,我也必须在克隆函数中设置它)谢谢! - Sam
你可以使用反射来构建一些东西(循环遍历所有字段并复制它们),但是从经验上来看,最好自己控制复制。有时你需要浅拷贝,对于某些对象,你需要深拷贝。 - GvS
为什么在上面的代码中 other = (A)this.MemberwiseClone(); 不能工作(而不是必须设置每个属性)。我试图避免设置每个属性或使用反射。 - PeterX

4

经过摸索和阅读大量资料,GvS和Jan所提供的两种解决方案都是可行的。

然而,我的最终目标是不想被强制在复制方法中逐一列出每个成员。

为什么: a) 如果编辑类并添加其他对象,则必须更新复制方法。如果其他人更新了该类,则他们可能会忘记这样做。

b) 可能有很多成员,给它们分配值可能很耗时。

c) 因为我很懒,所以感觉不太对劲。

幸运的是,我并不是唯一一个有相同想法的人。通过ValueInjector找到了一个非常简单的解决方案(这在这些论坛上已经讨论过很多次)。

获取dll之后(http://valueinjecter.codeplex.com/documentation),代码变成了:

A a = new A();
a.s1 = "...";


B b = new B();
b.InjectFrom(a);

这就是全部内容了 :)
显然,您需要包括:
using Omu.ValueInjecter;

并不要忘记将其添加到参考文献中。

3
你也可以使用JSON序列化器。例如,你可以在子类中添加一个静态方法,然后像这样调用它:
var porsche = Porsche.FromCar(basicCar);

在这里,“Porsche”是子类,“Car”是基类。函数可能会像这样:
public class Porsche : Car
{
    public static Porsche FromCar(Car basicCar)
    {
        // Create a JSON string that represents the base class and its current values.
        var serializedCar = JsonConvert.SerializeObject(basicCar);

        // Deserialize that base class string into the child class.
        return JsonConvert.DeserializeObject<Porsche>(serializedCar);
    }

    // Other properties and functions of the class...
}

这里的诀窍是,子类中可用但基类中不可用的属性将使用它们的默认值进行创建,通常为null,具体取决于属性的类型。反序列化也按照属性名称进行,因此所有属性都会被复制。

我没有测试过这段代码,但应该可以工作,因为我以前做过一两次类似的事情。希望能帮助到某个人。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接