我有一个类 A
和一个继承自类 A
并添加了一些字段的类 B
。
如果有一个类型为 A
的对象 a
,如何创建一个包含对象 a
所有数据的类型为 B
的对象 b
?
我尝试过 a.MemberwiseClone()
但这只会给我另一个类型为 A
的对象。而且我不能将 A
强制转换为 B
,因为继承关系只允许相反的转换。
正确的方法是什么?
我有一个类 A
和一个继承自类 A
并添加了一些字段的类 B
。
如果有一个类型为 A
的对象 a
,如何创建一个包含对象 a
所有数据的类型为 B
的对象 b
?
我尝试过 a.MemberwiseClone()
但这只会给我另一个类型为 A
的对象。而且我不能将 A
强制转换为 B
,因为继承关系只允许相反的转换。
正确的方法是什么?
语言本身没有内置自动实现这种操作的方式......
一个选项是向类B添加一个接受类A作为参数的构造函数。
然后你可以这样做:
B newB = new B(myA);
构造函数可以根据需要将相关数据进行复制。我会给A类添加一个复制构造函数,然后在B类中添加一个新的构造函数,该构造函数接受A类的实例,并将其传递给基类的复制构造函数。
public static TOut GetShallowCopyByReflection<TOut>(this Object objIn)
{
Type inputType = objIn.GetType();
Type outputType = typeof(TOut);
if (!outputType.Equals(inputType) && !outputType.IsSubclassOf(inputType)) throw new ArgumentException(String.Format("{0} is not a sublcass of {1}", outputType, inputType));
PropertyInfo[] properties = inputType.GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.FlattenHierarchy);
FieldInfo[] fields = inputType.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.FlattenHierarchy);
TOut objOut = (TOut)Activator.CreateInstance(typeof(TOut));
foreach (PropertyInfo property in properties)
{
try
{
property.SetValue(objOut, property.GetValue(objIn, null), null);
}
catch (ArgumentException) { } // For Get-only-properties
}
foreach (FieldInfo field in fields)
{
field.SetValue(objOut, field.GetValue(objIn));
}
return objOut;
}
这种方法会复制所有属性-私有和公共,以及所有字段。属性是按引用复制的,因此它是浅复制。
单元测试:
[TestClass]
public class ExtensionTests {
[TestMethod]
public void GetShallowCloneByReflection_PropsAndFields()
{
var uri = new Uri("http://www.stackoverflow.com");
var source = new TestClassParent();
source.SomePublicString = "Pu";
source.SomePrivateString = "Pr";
source.SomeInternalString = "I";
source.SomeIntField = 6;
source.SomeList = new List<Uri>() { uri };
var dest = source.GetShallowCopyByReflection<TestClassChild>();
Assert.AreEqual("Pu", dest.SomePublicString);
Assert.AreEqual("Pr", dest.SomePrivateString);
Assert.AreEqual("I", dest.SomeInternalString);
Assert.AreEqual(6, dest.SomeIntField);
Assert.AreSame(source.SomeList, dest.SomeList);
Assert.AreSame(uri, dest.SomeList[0]);
}
}
internal class TestClassParent
{
public String SomePublicString { get; set; }
internal String SomeInternalString { get; set; }
internal String SomePrivateString { get; set; }
public String SomeGetOnlyString { get { return "Get"; } }
internal List<Uri> SomeList { get; set; }
internal int SomeIntField;
}
internal class TestClassChild : TestClassParent {}
property.SetValue(objIn, property.GetValue(objIn, null), null);
应该改为:
property.SetValue(objOut, property.GetValue(objIn, null), null);
否则属性只会被设置回原始对象而不是新对象。只是想帮助一些复制粘贴者。 - briandunningtonReflection.Emit
来生成一个按需克隆方法,性能问题可以完全消除。 - Dai使用工厂方法模式:
private abstract class A
{
public int P1 { get; set; }
public abstract A CreateInstance();
public virtual A Clone()
{
var instance = CreateInstance();
instance.P1 = this.P1;
return instance;
}
}
private class B : A
{
public int P2 { get; set; }
public override A CreateInstance()
{
return new B();
}
public override A Clone()
{
var result = (B) base.Clone();
result.P2 = P2;
return result;
}
}
private static void Main(string[] args)
{
var b = new B() { P1 = 111, P2 = 222 };
var c = b.Clone();
}
public ClassB Convert(ClassA a)
{
ClassB b = new ClassB();
// Set the properties
return b;
}
你也可以为ClassB编写一个构造函数,使其接受一个ClassA对象。
不行,你不能这样做。实现这个的一种方法是在类B上添加一个接受类型为B的参数的构造函数,并手动添加数据。
所以你可以像这样:
public class B
{
public B(A a)
{
this.Foo = a.foo;
this.Bar = a.bar;
// add some B-specific data here
}
}
虽然没有人建议过这个方法(而且这并不适用于所有人),但应该说,如果您有从一开始就创建对象b的选项,请使用该选项,而不是创建对象a,然后将其复制到对象b。例如,想象一下您在同一个函数中,并且有以下代码:
var a = new A();
a.prop1 = "value";
a.prop2 = "value";
...
// now you need a B object instance...
var b = new B();
// now you need to copy a into b...
不必担心上一个被注释掉的步骤,只需从 b 开始设置值:
var b = new B();
b.prop1 = "value";
b.prop2 = "value";
我知道并不是每个人都会喜欢上面的内容。我遇到过很多程序员,他们太专注于自己的代码,以至于没有意识到一个更简单的解决方案就在眼前。 :)
public virtual T CreateObject<T>()
{
if (typeof(T).IsSubclassOf(this.GetType()))
{
throw new InvalidCastException(this.GetType().ToString() + " does not inherit from " + typeof(T).ToString());
}
T ret = System.Activator.CreateInstance<T>();
PropertyInfo[] propTo = ret.GetType().GetProperties();
PropertyInfo[] propFrom = this.GetType().GetProperties();
// for each property check whether this data item has an equivalent property
// and copy over the property values as neccesary.
foreach (PropertyInfo propT in propTo)
{
foreach (PropertyInfo propF in propFrom)
{
if (propT.Name == propF.Name)
{
propF.SetValue(ret,propF.GetValue(this));
break;
}
}
}
return ret;
}
然后如果你想从超类创建一个真实的子类对象,只需调用
this.CreateObject<subclass>();
搞定了!
class ClassA():
def __init__(self, **attrs):
self.__dict__.update(**attrs)
...
b = ClassA(**other_class.__dict__)
同时也可以与继承一起使用
class Fields(AbstractType):
def __init__(self, **attrs):
self.__dict__.update(**attrs)
my_date = Field(Date, required=True)
text_field = Field(String, required=True)
...
class MyClass(InputObjectType, Fields):
pass
class MyClassWithError(ObjectType, Fields):
error = Field(String, required=True)
error_class = MyClassWithError(**my_class.__dict__)
error_class.error = "my error description"