代码之家  ›  专栏  ›  技术社区  ›  Daniel Möller

基于属性复制派生类的实例

  •  2
  • Daniel Möller  · 技术社区  · 6 年前

    假设我有一个抽象基类, CreateCopy 方法:

    public abstract class BaseClass
    {
        ///base stuff
    
        public BaseClass CreateCopy() //or public object, if necessary   
        {
            //?????
        }
    }
    

    假设所有派生类都有一个无参数的构造函数和属性字段(可以用某种属性标记):

    public class DerivedClass : BaseClass
    {
        [CopiableProperty]
        public string Property1 {get; private set;}
    
        [CopiableProperty]
        public int Property2 {get; private set;}
    
        //no need to copy
        public int Property3 {get; private set;}
    
        //parameterless constructor
        public DerivedClass() { }
    }
    

    有没有可能用这个结构来写 CreateCopy() 在某种程度上,我可以用正确的 CopiableProperty 领域?


    当然我可以做一个 public abstract BaseClass CreateCopy() 并强制每个派生类处理自己的副本,但由于派生类的大小和数量,这将带来太多额外的工作。

    3 回复  |  直到 6 年前
        1
  •  4
  •   Camilo Terevinto Chase R Lewis    6 年前

    一个相当简单的方法可以是使用泛型和反射:

    public abstract class BaseClass
    {
        // restrict to children of BaseClass
        public T CreateCopy<T>() where T: BaseClass, new()
        {
            var copy = new T();
    
            // get properties that you actually care about
            var properties = typeof(T).GetProperties()
                .Where(x => x.GetCustomAttribute<CopiablePropertyAttribute>() != null);
    
            foreach (var property in properties)
            {
                // set the value to the copy from the instance that called this method
                property.SetValue(copy, property.GetValue(this));
            }
    
            return copy;
        }
    }
    
    public class DerivedClass : BaseClass
    {
        [CopiableProperty]
        public string Property1 { get; set; }
    
        [CopiableProperty]
        public int Property2 { get; set; }
    
        public int Property3 { get; set; }
    
        public override string ToString()
        {
            return $"{Property1} - {Property2} - {Property3}";
        }
    }
    
    static void Main(string[] args)
    {
        var original = new DerivedClass
        {
            Property1 = "Hello",
            Property2 = 123,
            Property3 = 500
        };
    
        var copy = original.CreateCopy<DerivedClass>();
    
        Console.WriteLine(original);
        Console.WriteLine(copy);
    
        Console.ReadLine();
    }
    

    这将打印:

    Hello - 123 - 500
    Hello - 123 - 0
    

    另一种方法是利用序列化库,如果您不介意依赖关系:

    public abstract class BaseClass
    {
        public BaseClass CreateCopy()
        {
            string serialized = JsonConvert.SerializeObject(this);
    
            var actualType = GetType();
    
            return JsonConvert.DeserializeObject(serialized, actualType) as BaseClass;
        }
    }
    
    public class DerivedClass : BaseClass
    {
        public string Property1 { get; set; }
    
        public int Property2 { get; set; }
    
        [JsonIgnore]
        public int Property3 { get; set; }
    
        //parameterless constructor
        public DerivedClass() { }
    
        public override string ToString()
        {
            return $"{Property1} - {Property2} - {Property3}";
        }
    }
    
        2
  •  2
  •   Rui Jarimba    6 年前

    我的解决方案使用序列化/反序列化,使用json.net nuget包。

    不需要基类中的方法,可以改用扩展方法( adapted from this answer )以下内容:

    using Newtonsoft.Json;
    
    public static class ObjectExtensions
    {
        public static T Clone<T>(this T source)
        {
            var serialized = JsonConvert.SerializeObject(source);
            var clone = JsonConvert.DeserializeObject<T>(serialized);
    
            return clone;
        }
    }
    

    然后使用属性控制是否应复制哪些属性-例如:

    using Newtonsoft.Json;
    
    public class DerivedClass : BaseClass
    {
        public string Property1 { get; set; }
    
        public int Property2 { get; set; }
    
        [JsonIgnore]
        public int Property3 { get; set; }
    }
    

    使用代码:

    var obj1 = new DerivedClass
    {
        Property1 = "Abc",
        Property2 = 999,
        Property3 = 123
    };
    
    DerivedClass clone = obj1.Clone();
    

    结果-如您所见,property3在克隆的对象中具有默认值:

    Clone results

        3
  •  1
  •   Aleks Andreev Md. Suman Kabir    6 年前

    遍历类型中的所有属性,并使用 GetCustomAttributes 是的。见代码:

    public BaseClass CreateCopy()
    {
        var type = GetType();
        var result = Activator.CreateInstance(type);
    
        foreach (var propertyInfo in type.GetProperties())
        {
            var skipThisProperty = !propertyInfo.GetCustomAttributes(
                    typeof(CopiablePropertyAttribute), false)
                .Any();
    
            if (skipThisProperty)
                continue;
    
            var value = propertyInfo.GetValue(this, null);
            propertyInfo.SetValue(result, value, null);
        }
    
        return (BaseClass) result;
    }
    

    请注意 null 参数输入 GetValue SetValue 是的。如果属性是索引器,则需要传递正确的值作为最后一个参数