如何基于父类实例创建子类构造函数?

Cœu*_*œur 5 c# inheritance constructor clone base-class

我有一个Item和一个子类AdvancedItem(如果重要的话,所有都由值类型组成):

public Item
{
    public string A;
    public bool B;
    public char C;
    ...// 20 fields
}

public AdvancedItem : Item
{
    public string Z;
}
Run Code Online (Sandbox Code Playgroud)

单独创建Item或AdvancedItem很容易:

var item = new Item { A = "aa", B = true, C = 'c', ... };
var aItem = new AdvancedItem { A = "aa", B = true, C = 'c', ..., Z = "zz" };
Run Code Online (Sandbox Code Playgroud)

现在,我只想通过分别提供字符串Z将Item转换为AdvancedItem.为了实现这一点,我正在考虑使用构造函数.

尝试A:

// annoying, we are not using the inheritance of AdvancedItem:Item
// so we will need to edit this whenever we change the class Item
public AdvancedItem(Item item, string z)
{
    A = item.A;
    B = item.B;
    ...;//many lines
    Z = z;
}
Run Code Online (Sandbox Code Playgroud)

尝试B:

// to use inheritance it seems I need another constructor to duplicate itself
public Item(Item item)
{
    A = item.A;
    B = item.B;
    ...;//many lines
}

public AdvancedItem(Item item, string z) : base(Item)
{
    Z = z;
}
Run Code Online (Sandbox Code Playgroud)

有没有办法改进第二次尝试,以避免写多行X = item.X也许是一个自动克隆或自动复制一个类的解决方案,public Item(Item item)它将在一行中写入?

Bas*_*Bas 6

考虑使用AutoMapper复制对象之间的属性.

这将允许以下内容:

Item a = new Item { A = 3, B = 'a', .... };

AdvancedItem advanced= Mapper.Map<AdvancedItem>(a);
string z = "Hello World";
advanced.Z = z;
Run Code Online (Sandbox Code Playgroud)

更新

如果你不想使用AutoMapper你可以使用Reflection或更好,Expressions.但是,这会使您的代码更复杂一些

考虑以下两种类型:

class Item
{
    public int A, B, C;
    public string D, E, F;
    private int privateInt;

    public Item(int valueOfPrivateField)
    {
        privateInt = valueOfPrivateField;
    }
}

class AdvancedItem : Item
{
    public string G;

    public AdvancedItem(int valueOfPrivateField) : base(valueOfPrivateField)
    {
    }
}
Run Code Online (Sandbox Code Playgroud)

我们可以定义一个创建字段副本表达式的方法.既然你提到你的所有字段都是值类型,我们可以将每个字段逐个复制到另一个对象:

    private static void MapFields<T>(T target, T source)
    {
        Type type = typeof (T);
        if (!Mappers.ContainsKey(type))
        {
            //build expression to copy fields from source to target;
            var targetParam = Expression.Parameter(typeof(object));
            var targetCasted = Expression.TypeAs(targetParam, typeof(T));
            var sourceParam = Expression.Parameter(typeof(object));
            var sourceCasted = Expression.TypeAs(sourceParam, typeof(T));
            var setters = new List<Expression>();
            //get all non-readonly fields
            foreach (var fieldInfo in typeof(T).GetFields(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public).Where(f => !f.IsInitOnly))
            {
                Expression targetField = Expression.Field(targetCasted, fieldInfo);
                Expression sourceField = Expression.Field(sourceCasted, fieldInfo);
                setters.Add(Expression.Assign(targetField, sourceField));
            }
            Expression block = Expression.Block(setters);

            var mapperFunction = Expression.Lambda<Action<object, object>>(block, targetParam,
                sourceParam).Compile();
            Mappers[type] = mapperFunction;
        }
        Mappers[type](target, source);

    }

    private static readonly Dictionary<Type, Action<object, object>> Mappers =
        new Dictionary<Type, Action<object, object>>();
Run Code Online (Sandbox Code Playgroud)

这会缓存从源到目标对象映射所有字段的函数,并且应该具有与手动编写this.A = A, this.B = B等相同的性能.

调用方法:

    static void Main(string[] args)
    {
        var item = new Item(56) {A = 5, B = 6};

        var advanced = new AdvancedItem(0);

        MapFields(advanced, item);
        int a = advanced.A; //5
        int b = advanced.B; //6;
        //note that advanced.privateInt == 56!
    }
Run Code Online (Sandbox Code Playgroud)

请注意,此代码比AutoMapper更复杂且更不可靠,不建议或准备好用于生产系统.

  • 但是比**使用**自动映射器更复杂;) (2认同)