在C#中将父对象转换为子对象

27

嗨,我想在C#中将父对象转换为子对象。

public class Parent
{
    public string FirstName {get; set;}
    public string LastName {get; set;}
    public string City {get; set;}
}

public class Child : Parent
{
    public string PhoneNumber {get; set;}
    public string MobileNumber {get; set;}
}

现在的情况是有一个父对象列表,我想生成一个子对象列表,以便我可以获得更多的扩展信息。

List<Parent> lstParent;
List<Child> lstChild = new List<Child>();

foreach(var obj in lstParent)
{
    lstChild.add((Child)obj);
}

作为子类继承了父类,因此子类已经拥有了父类成员。我只想自动填充它们,以便可以填充子类的datamember。

7个回答

33
我这样做(这只是一个例子):
using System.Reflection;

public class DefaultObject
{
    ...
}

public class ExtendedObject : DefaultObject
{
    ....
    public DefaultObject Parent { get; set; }

    public ExtendedObject() {}
    public ExtendedObject(DefaultObject parent)
    {
        Parent = parent;

        foreach (PropertyInfo prop in parent.GetType().GetProperties())
            GetType().GetProperty(prop.Name).SetValue(this, prop.GetValue(parent, null), null);
    }
}

使用:

DefaultObject default = new DefaultObject { /* propery initialization */ };
ExtendedObject extended = new ExtendedObject(default); // now all properties of extended are initialized by values of default properties.
MessageBox.Show(extended.Parent.ToString()); // now you can get reference to parent object

2
为什么不直接使用 prop.SetValue(this, prop.GetValue(parent)); 呢? - Andrew
@Andrew 我已经挖掘了一段时间,但仍然找不到为什么前者更可取的答案。如果有真正的原因,我很想知道为什么,这样我就可以更好地理解这个机制;目前我看不出 prop(由 parent.GetType().GetProperties() 返回的数组的 PropertyInfo 元素)如何与 GetType().GetProperty(prop.Name)(使用 propName 查找的 PropertyInfo)有所不同,这让我对为什么在这里使用冗长版本感到困惑。 - zcoop98
可能是因为Aleksey已经离开了几年,并且从未修订过这个答案。我猜他在一些努力之后才得到了那个工作代码,并没有意识到它可以简化。如果我的版本运行正常,我肯定会选择这种方式。 - Andrew

25

如果我正确理解了您的"我只想自动填充它们"的评论,那么您希望创建一个新的Child对象,该对象填充了Parent的值,并且新属性有默认值。最好的方法是创建一个复制值的构造函数:

public class Parent
{
   public string FirstName {get; set;}
    public string LastName {get; set;}
    public string City {get; set;}
}

public class Child : Parent
{
    public string PhoneNumber {get; set;}
    public string MobileNumber {get; set;}

    public Child (Parent parentToCopy)
    {
        this.FirstName = parentToCopy.FirstName;
        this.LastName = parentToCopy.LastName;
        this.City = parentToCopy.City;

        this.PhoneNumber = string.Empty; // Or any other default.
        this.MobileNumber = string.Empty;
    } 
}

现在你可以像上面的答案一样使用LINQ,来创建每个父对象的子对象:

List<Child> lstChild = lstParent.Select(parent => new Child(parent)).ToList();

注意,这与@daryal的回答非常相似,但它将从父级到子级的复制逻辑封装在构造函数中,而不是在new Child()调用中外部实现。


9
这种方法的缺点在于维护。如果您在父类中添加一个字段,还必须意识到要在子类的构造函数中插入赋值。在这种情况下,这似乎非常简单。但在更复杂的环境中,这可能会被遗忘。这就是为什么我更喜欢使用反射的方法。 - Fabian Bigler

7
这是我从解决方案中想出来的。
    public static void ShallowConvert<T, U>(this T parent, U child)
    {
        foreach (PropertyInfo property in parent.GetType().GetProperties())
        {
            if (property.CanWrite)
            {
                property.SetValue(child, property.GetValue(parent, null), null);
            }
        }
    }

1
为什么不使用其他重载,如 property.SetValue(child, prop.GetValue(parent)); 呢? - Andrew
1
在我看来,这是这里最好的答案,再加上@Andrew的有用评论。 - EvilDr

6
我这样做了:
class Parent
{
  ...
}

class Child :Parent
{
  ...
  public Child(Parent p)
  {
            foreach (FieldInfo prop in  p.GetType().GetFields())
                GetType().GetField(prop.Name).SetValue(this, prop.GetValue( p));

            foreach (PropertyInfo prop in  p.GetType().GetProperties())
                GetType().GetProperty(prop.Name).SetValue(this, prop.GetValue( p, null), null);
  }
}

1
为什么不直接使用 prop.SetValue(this, prop.GetValue(p)); 呢? - Andrew

3
var lstChild = lstParent.Cast<Child>().ToList();

或者

var lstChild = lstParent.ConvertAll(x=>(Child)x);

但是这两种做法都假设Parent列表实际上包含Child实例。你无法更改对象的实际类型。


嗯...这只是一个类型转换。后代特定的字段将无法正确初始化。你可能会遇到NullReferenceException hive。 - Yury Schkatula
2
@YurySchkatula 不会,它要么会因为类型转换异常而失败,要么会正常工作;OP请求了一个转换;这就是一个转换。既不需要也不执行任何初始化。 - Marc Gravell

2
你也可以使用反射,但这对你的情况来说更简单。
foreach(var obj in lstParent)
{
    Child child = new Child(){ FirstName = obj.FirstName, LastName=obj.LastName, City = obj.City};
    child.MobileNumber = "some mobile number";
    child.PhoneNumber = "some phone number";
    lstChild.Add((Child)obj);
}

0

另一种解决方案是让父级控制复制逻辑,子级将复制源传递到基类构造函数中。

例如,在Avner的解决方案上进行迭代

public class Parent
{
    public string FirstName {get; set;}
    public string LastName {get; set;}
    public string City {get; set;}

    public Parent()
    {
    }

    public Parent(Parent copyFrom)
    {
        this.FirstName = copyFrom.FirstName;
        this.LastName = copyFrom.LastName;
        this.City = copyFrom.City;
    }
}

public class Child : Parent
{
    public string PhoneNumber {get; set;}
    public string MobileNumber {get; set;}

    public Child (Parent parentToCopy) : base(parentToCopy)
    {
    } 
}

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