C# - 如何将一个对象复制/创建为子级

4

这个例子可以更好地解释。假设有以下两个类:

public class MyClassA
{
    public String Property_A { get; set; }
    public String Property_B { get; set; }
    public String Property_C { get; set; }
    public String Property_D { get; set; }
    ...
    public String Property_Y { get; set; }
}

public class MyClassB: MyClassA
{
    public String Property_Z { get; set; }
}

假设我已经完全创建了MyClassA的实例(填入了所有属性从A到Y)。然后我需要创建一个MyClassB的实例,它与我的MyClassA实例完全相同,但是Property_Z被填充了(当然是自定义值)。我该怎么做呢?
这样做不起作用(抛出无效转换异常):
MyClassB myInstanceB = (myClassB) myInstanceA;
myInstance.Property_Z = myCustomValue;

自从我的C++时代以来,我就没有需要做这样的事情了,我被难住了。

有什么想法吗?


更新:我在如何创建实例方面找到了解决办法。它在下面。我没有将其标记为答案,因为它并不完全符合我的问题。

7个回答

5
您创建的实例是一个 MyClassA。这是它的运行时类型,而不是 MyClassB。您不能在运行时将 MyClassA 实例强制转换为 MyClassB,因为 MyClassB 是比 MyClassA 更具体的类型。
您需要创建一个全新的 MyClassB 实例。一种简化方法是创建一个接受 MyClassA 的构造函数,例如:
public class MyClassB : MyClassA
{
    public MyClassB(MyClassA a, string z)
    {
        this.PropertyA = a.PropertyA;
        this.PropertyB = a.PropertyB;
        // etc.
        this.PropertyZ = z;
    }

    public string PropertyZ { get; set; }
}

我没有说过这句话,但我希望避免不得不从一个实例复制所有项目到另一个实例的情况。 - Vaccano
@Vaccano:如果你发现自己不得不在各个地方都这样做,那么我建议你重新考虑你的设计;但是,如果你迫切需要加快这个过程,那么你可以使用反射或基于反射的映射器,例如AutoMapper - 只要知道你节省的编码时间将被程序中增加的执行时间所抵消(具体取决于你执行的频率)。 - Aaronaught
是的,我也不想减慢执行速度。我找到了一个折中方案(请参见我的回答),通过更改如何设置myInstanceA来使用类型参数。我没有像我希望的那样将myInstanceA转换为MyClassB,但它可以工作。(我不会将我的答案设置为“答案”,因为它并不完全符合我提出的问题。) - Vaccano

2
您可以使用反射来复制基类属性,如此处所示。
 public void Update(MyObject o)
    {
        MyObject copyObject = ...
        Type type = o.GetType();
        while (type != null)
        {
            UpdateForType(type, o, copyObject);
            type = type.BaseType;
        }
    }


    private static void UpdateForType(Type type, MyObject source, MyObject destination)
    {
        FieldInfo[] myObjectFields = type.GetFields(
            BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance);

        foreach (FieldInfo fi in myObjectFields)
        {
            fi.SetValue(destination, fi.GetValue(source));
        }
    }

2
直截了当的回答是:
public class MyClassA 
{ 
    public String Property_A { get; set; } 
    public String Property_B { get; set; } 
    public String Property_C { get; set; } 
    public String Property_D { get; set; } 
    ... 
    public String Property_Y { get; set; } 
} 

public class MyClassB: MyClassA 
{ 
    public MyClassB(MyClassA copy)
    {
        Property_A = copy.PropertyA;
        Property_B = copy.PropertyB;
        ...
    }
    public String Property_Z { get; set; } 
} 

使用方法如下:

MyClassB o = new MyClassB(instanceOfMyClassA);
o.Property_Z = whatever;

2

1
你可以定义从MyClassA到MyClassB的显式或隐式转换,并使用你提供的语法。
public class MyClassB : MyClassA
{
    public String Property_Z { get; set; }

    public static explicit operator MyClassB(MyClassA a)
    {
        MyClassB b = new MyClassB();
        b.Property_A = a.Property_A;
        /* ... */
        b.Property_Y = a.Property_Y;
        return b;
    }
}

0

这是我最终做的:

我创建了一个方法,它可以使用类型参数来设置所有属性(A-Y)并进行初始化。代码如下:

public T MakeAMyClass<T>(AnotherClass 
where T: MyClassA : new()
{
   T returnValue = new T();
   T.Property_A = somethingToSetFrom.MakeAPropertyA();
   // Fill in the rest of the properties
}

由于类型参数可以是从约束继承下来的类型,因此我可以传递 MyClassB 对象并像 myClassA 一样使它们被创建。

然后我可以根据需要制作任何一个。

MyClassA myInstanceA = MakeAMyClass<MyClassA>(somethingToSetFrom);
MyClassB myInstanceB = MakeAMyClass<MyClassB>(somethingToSetFrom);
myInstanceB.Property_Z = someotherValue;

这让我避免了需要创建一个方法来复制MyClassA到MyClassB所有属性的麻烦。


0

怎么样:

  1. 创建一个实现IClonable接口并具有所有字符串属性A到D和Z的基类。
  2. 为MyClassA创建此类的实例。
  3. 通过克隆MyClassA创建MyClassB的实例。
  4. 设置MyClassB中的属性Z。

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