2009-07-07 16 views
19

Ich habe eine Klasse A und eine Klasse B, die Klasse A erbt und es mit einigen weiteren Feldern erweitert.Wie "klon" ein Objekt in ein Unterklassenobjekt?

Nachdem ein Objekt a vom Typ A, wie kann ich ein Objekt erstellen b vom Typ B, die alle Daten enthalten, die a enthaltenes Objekt?

Ich habe versucht a.MemberwiseClone(), aber das gibt mir nur einen anderen Typ A Objekt. Und ich kann A in B nicht werfen, da die Vererbungsbeziehung nur die gegenteilige Besetzung erlaubt.

Was ist der richtige Weg?

+0

Danke für die Antworten. Ich habe nach einem automatischen Weg gesucht, aber du schlägst vor, dass es keinen solchen gibt. :( – Vizu

+1

Leider, nein. Sie müssen einen Konstruktor oder eine Fabrik Methode eines Formulars hinzufügen. –

+0

@Vizu welche Methode haben Sie adoptiert, ich möchte auch etwas ähnliches, bitte legen Sie hier, wenn Sie es haben –

Antwort

8

Es gibt kein Mittel, diese automatisch gebaut in die Sprache zu tun ...

Eine Möglichkeit ist, einen Konstruktor Klasse B hinzuzufügen, die eine Klasse A als Argument.

Dann könnten Sie tun:

B newB = new B(myA); 

Der Konstruktor kann kopieren Sie einfach die relevanten Daten über je nach Bedarf, in diesem Fall.

+0

ist es möglich, eine Unterklasse zu Super-Klasse mit einigen der ähnlichen Feilds kopieren –

1

Erstellen Sie einen Ctor in B, der es ermöglicht, ein Objekt vom Typ A zu übergeben, kopieren Sie dann die A-Felder und legen Sie die B-Felder entsprechend fest.

0

Sie könnten eine Konvertierungsmethode für Klasse B erstellen, die die Basisklasse akzeptiert.

Sie könnten auch einen Konstruktor für ClassB haben, um ein Objekt von ClassA aufzunehmen.

11

Ich würde einen Kopierkonstruktor zu A hinzufügen und dann B einen neuen Konstruktor hinzufügen, der eine Instanz von A übernimmt und an den Kopierkonstruktor der Basis übergibt.

+2

Die akzeptierte Antwort ist, was ich immer getan habe, aber Diese Wendung ist einfach und elegant. – JMD

0

Nein, das geht nicht. Eine Möglichkeit, dies zu erreichen, besteht darin, einen Konstruktor für Klasse B hinzuzufügen, der einen Parameter vom Typ B akzeptiert und Daten manuell hinzufügt.

So könnte man so etwas wie dieses:

public class B 
{ 
    public B(A a) 
    { 
    this.Foo = a.foo; 
    this.Bar = a.bar; 
    // add some B-specific data here 
    } 
} 
+3

Ich stimme nicht zu, dass Sie eine Clone() -Methode auf A haben sollten, die ein B zurückgibt, da dies eine zirkuläre Abhängigkeit einführt. –

+0

Ich stimme dem Konstruktor für Klasse B zu, aber warum brauchen Sie das CloneToB() Methode? –

+0

ehm yeah du hast recht, ich hätte diese Methode nicht einschließen sollen.Ich habe es eingefügt, weil das Poster MemberWiseCl erwähnt ein(). Wie auch immer, dieser Code ist nicht gut und ich werde ihn löschen. Vielen Dank. – Razzie

2

Mit Factory Method Pattern:

private abstract class A 
    { 
     public int A1 { get; set; } 

     public abstract A CreateInstance(); 

     public virtual A Clone() 
     { 
      var instance = CreateInstance(); 
      instance.A1 = A1; 
      return instance; 
     } 
    } 

    private class B : A 
    { 
     public int A3 { get; set; } 

     public override A CreateInstance() 
     { 
      return new B(); 
     } 

     public override A Clone() 
     { 
      var result = (B) base.Clone(); 
      result.A3 = A3; 
      return result; 
     } 
    } 

    private static void Main(string[] args) 
    { 
     var b = new B() { A1 = 1, A3 = 2 }; 

     var c = b.Clone(); 
    } 
3

Sie können dies erreichen, indem sie mithilfe von Reflektion.

Vorteil: Wartbarkeit. Keine Notwendigkeit, den Kopierkonstruktor oder ähnliches zu ändern, Eigenschaften hinzuzufügen oder zu entfernen.

Nachteil: Leistung. Die Reflexion ist langsam. Wir sprechen jedoch immer noch Millisekunden bei durchschnittlich großen Klassen.

Hier ist ein Spiegelbild-basierte flache Kopie Implementierung unterstützt copy-to-Unterklasse, die Erweiterung Methoden:

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; 
} 

Diese Methode wird alle Eigenschaften kopieren - privat und öffentlich, sowie alle Felder aus. Eigenschaften werden durch Verweis kopiert, wodurch es zu einer flachen Kopie wird.

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 {} 
+0

Was zählt als durchschnittliche Klassengröße? –

+0

@ adam-l-s Hehe gute Frage. Meine Antwort wie immer, wenn es um die Performance geht: Messen. Wenn es schnell genug für dich ist, benutze es. Es wird gesagt, dass Reflection 1000-mal langsamer ist als der normale Zugriff auf Eigenschaften: http://stackoverflow.com/questions/25458/how-costly-is-net-reflection – Nilzor

0

in Ihrer Basisklasse hinzufügen, die virtuelle Methode Create unten ...

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; 
    } 

dann sagen Sie, ein richtiges Leben Unterklasse-Objekt aus der Superklasse erstellen möchten einfach Anruf

this.CreateObject<subclass>(); 

Das sollte es tun!

0

Obwohl niemand dies vorgeschlagen hat (und das wird zugegebenermaßen nicht für alle funktionieren), sollte gesagt werden, dass wenn Sie die Möglichkeit haben, Objekt b von Anfang an zu erstellen, anstatt Objekt a zu erstellen Kopieren in Objekt b. Zum Beispiel vorstellen, dass Sie in der gleichen Funktion und haben diesen 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... 

Statt über diese letzte kommentierte Schritt der sich Gedanken, nur mit b beginnen und die Werte gesetzt:

var b = new B(); 
b.prop1 = "value"; 
b.prop2 = "value"; 

Ziehen Sie bitte an 't downvote mich, weil Sie das oben genannte für dumm halten! Ich bin auf viele Programmierer gestoßen, die sich so sehr auf ihren Code konzentriert haben, dass sie nicht wussten, dass eine einfachere Lösung ihnen ins Gesicht schaut. :)

Verwandte Themen