【问题标题】:C# inheritance. Derived class from Base classC#继承。从基类派生的类
【发布时间】:2011-12-25 22:28:28
【问题描述】:

我有一个基类

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 代码采用上述两种方式 - 但我遇到运行时错误

请帮忙

【问题讨论】:

  • 克隆时要小心——特别是如果你的类有可变引用类型的字段。决定是要深度克隆还是浅层克隆,并记录下来。
  • 正确。这个特定的类没有引用,所以浅克隆适用于它。我在这里为任何感兴趣的人找到了一篇关于浅克隆与深度克隆的好帖子:itpksingh.blogspot.com/2009/08/…
  • 找到了使用 ValueInjector 的解决方案。 StackOverFlow 还不允许我“回答我自己的问题”。一旦完成,将发布完整的详细信息。

标签: c# inheritance polymorphism


【解决方案1】:

您遇到的基本问题是,您必须构造一个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:问题是,你必须构造一个 B 类型的对象。因为 A 是 B 的一部分,你必须找到一种方法来设置新 B 实例的属性,即属于 A 类型。您的 clone A 方法将不起作用,因为这会为您提供 A 类型的实例,而您需要 B 类型的实例!您可以做的是在 A 的构造函数中使用反射。我将更新我的答案以反映这一点。
  • 如果属性没有设置器,反射方法将抛出。检查 propInf.CanWrite 解决了问题
【解决方案2】:

您可以在 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 等)。如果该类有多个成员,无论如何要避免对每个成员进行编码? (如果我决定添加另一个成员,我也必须在克隆功能中设置它)
  • 你可以用反射构建一些东西(循环所有字段,并复制它们),但根据我知道的经验,你最好自己控制复制。有时您需要浅拷贝,而对于某些对象则需要深拷贝。
  • 为什么other = (A)this.MemberwiseClone(); 不能在上面工作(而不是必须设置每个属性)。我试图避免设置每个属性或使用反射。
【解决方案3】:

在玩耍并阅读了我能看到的所有内容之后,GvS 和 Jan 的上述两种解决方案都有效。 但是,我想要达到的最终结果并不是在 Copy 方法中强制写出每个成员。

为什么: 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;

不要忘记将其添加到引用中。

【讨论】:

    【解决方案4】:

    例如,您还可以使用 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,具体取决于属性的类型。反序列化也通过属性的名称进行,因此所有属性都被复制。

    我没有测试这段代码,但它应该可以工作,因为我以前做过一两次。希望它可以帮助某人。

    【讨论】:

      猜你喜欢
      • 2012-12-10
      • 2011-01-08
      • 2017-04-14
      • 1970-01-01
      • 1970-01-01
      • 2021-08-24
      • 2016-07-03
      • 2014-12-23
      • 2013-05-22
      相关资源
      最近更新 更多