假设我有一个抽象基类,我想要一个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()
并强制每个派生类照顾自己的副本,但是由于派生类的大小和数量,这会带来过多的额外工作。
答案 0 :(得分:4)
一种相当简单的方法可以使用泛型和反射:
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}";
}
}
答案 1 :(得分:2)
我的解决方案通过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在克隆对象中具有默认值:
答案 2 :(得分:1)
遍历类型中的所有属性,并使用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
参数。如果您的属性是索引器,则需要传递正确的值作为最后一个参数