问题
I have a class A and a class B that inherits class A and extends it with some more fields.
Having an object a of type A, how can I create an object b of type B that contains all data that object a contained?
I have tried a.MemberwiseClone() but that only gives me another type A object. And I cannot cast A into B since the inheritance relationship only allows the opposite cast.
What is the right way to do this?
回答1:
There is no means of doing this automatically built into the language...
One option is to add a constructor to class B that takes a class A as an argument.
Then you could do:
B newB = new B(myA);
The constructor can just copy the relevant data across as needed, in that case.
回答2:
I would add a copy constructor to A, and then add a new constructor to B that takes an instance of A and passes it to the base's copy constructor.
回答3:
You can achieve this by using reflection.
Advantage: Maintainability. No need for changing copy-constructor or similar, adding or removing properties.
Disadvantage: Performance. Reflection is slow. We're still talking milliseconds on average sized classes though.
Here's a reflection-based shallow copy implementation supporting copy-to-subclass, using extension methods:
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(objIn, property.GetValue(objIn, null), null);
}
catch (ArgumentException) { } // For Get-only-properties
}
foreach (FieldInfo field in fields)
{
field.SetValue(objOut, field.GetValue(objIn));
}
return objOut;
}
This method will copy all properties - private and public, as well as all fields. Properties are copied by reference, making it a shallow copy.
Unit tests:
[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 {}
回答4:
Using Factory Method Pattern:
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();
}
回答5:
Create a ctor in B that allows one to pass in an object of type A, then copy the A fields and set the B fields as appropriate.
回答6:
You could make a Convert method on class B that takes in the base class.
public ClassB Convert(ClassA a)
{
ClassB b = new ClassB();
// Set the properties
return b;
}
You could also have a constructor for ClassB take in an object of ClassA.
回答7:
No, you can't do that. One way to achieve this is to add a constructor on class B that accepts a parameter of type B, and add data manually.
So you could have something like this:
public class B
{
public B(A a)
{
this.Foo = a.foo;
this.Bar = a.bar;
// add some B-specific data here
}
}
回答8:
In your base class add the CreateObject virtual method below...
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;
}
then say you want to create a real life subclass object from the super class just call
this.CreateObject<subclass>();
That should do it!
回答9:
While no one suggested this (and this won't work for everyone, admittedly), it should be said that if you have the option of creating object b from the get-go, do that instead of creating object a then copying to object b. For example, imagine that you are in the same function and have this code:
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...
Instead of worrying about that last commented step, just start with b and set the values:
var b = new B();
b.prop1 = "value";
b.prop2 = "value";
Please don't downvote me because you think the above is stupid! I have encountered many programmers who are so focused on their code that they didn't realize a simpler solution is staring them in the face. :)
来源:https://stackoverflow.com/questions/1093056/how-to-clone-an-object-into-a-subclass-object