web-dev-qa-db-de.com

Die beste Möglichkeit, eine Instanz eines untergeordneten Objekts aus einem übergeordneten Objekt zu erstellen

Ich erstelle ein untergeordnetes Objekt aus einem übergeordneten Objekt. Das Szenario ist also, dass ich ein Objekt und ein untergeordnetes Objekt habe, das eine Entfernungseigenschaft für Szenarien hinzufügt, in denen ich suchen möchte. Ich habe mich für die Vererbung entschieden, da meine Benutzeroberfläche äquivalent mit einem Suchobjekt oder einer Liste von Objekten arbeitet, die nicht das Ergebnis einer Ortssuche sind. In diesem Fall scheint Vererbung eine vernünftige Wahl zu sein. 

Als Geschenk muss ich ein neues Objekt MyObjectSearch aus einer Instanz von MyObject generieren. Zur Zeit mache ich das im Konstruktor manuell, indem ich die Eigenschaften einzeln festlege. Ich könnte Reflektion gebrauchen, aber das wäre langsam. Gibt es einen besseren Weg, um diese Art der Objektverbesserung zu erreichen?

Hoffentlich illustriert mein Code das Szenario.

public class MyObject {

    // Some properties and a location.
}

public class MyObjectSearch : MyObject {

    public double Distance { get; set; }

    public MyObjectSearch(MyObject obj) {
         base.Prop1 = obj.Prop1;
         base.Prop2 = obj.Prop2;
    }
}

Und meine Suchfunktion:

public List<MyObjectSearch> DoSearch(Location loc) { 
  var myObjectSearchList = new List<MyObjectSearch>();       

   foreach (var object in myObjectList) {
       var distance = getDistance();
       var myObjectSearch = new MyObjectSearch(object);
       myObjectSearch.Distance = distance;
       myObjectSearchList.add(myObjectSearch);
   } 
   return myObjectSearchList;

}

19
Captain John

Die Basisklasse muss einen Kopierkonstruktor definieren:

public class MyObject
{
    protected MyObject(MyObject other)
    {
        this.Prop1=other.Prop1;
        this.Prop2=other.Prop2;
    }

    public object Prop1 { get; set; }
    public object Prop2 { get; set; }
}

public class MyObjectSearch : MyObject
{

    public double Distance { get; set; }

    public MyObjectSearch(MyObject obj)
         : base(obj)
    {
        this.Distance=0;
    }
    public MyObjectSearch(MyObjectSearch other)
         : base(other)
    {
        this.Distance=other.Distance;
    }
}

Auf diese Weise wird die Einstellung von Eigenschaften für alle abgeleiteten Klassen von der Basisklasse behandelt.

25
ja72

Es gibt leider keine einfache Möglichkeit, dies zu tun. Wie Sie sagten, müssten Sie entweder Reflection verwenden oder eine "Clone" -Methode erstellen, die ein neues untergeordnetes Objekt mithilfe eines übergeordneten Objekts als Eingabe generiert.

public class MyObjectSearch : MyObject {

    // Other code

    public static MyObjectSearch CloneFromMyObject(MyObject obj)
    {
        var newObj = new MyObjectSearch();

        // Copy properties here
        obj.Prop1 = newObj.Prop1;

        return newObj;
    }
}

Egal was, Sie schreiben entweder Reflexionscode (der langsam ist) oder schreiben jede Eigenschaft von Hand. Es hängt alles davon ab, ob Sie Wartbarkeit (Reflektion) oder Geschwindigkeit (manuelle Eigenschaftskopie) wünschen oder nicht.

4
qJake

Sie können Reflection verwenden, um Eigenschaften zu kopieren.

public class ChildClass : ParentClass
{


    public ChildClass(ParentClass ch)
    {
        foreach (var prop in ch.GetType().GetProperties())
        {
            this.GetType().GetProperty(prop.Name).SetValue(this, prop.GetValue(ch, null), null);
        }
    }
}
0
mehdi

Eine generische Lösung wäre die Serialisierung in Json und zurück. In der Json-Zeichenfolge sind keine Informationen über den Klassennamen enthalten, von dem aus es serialisiert wurde. Die meisten Leute machen dies in Javascript. 

Wie Sie sehen, funktioniert es gut für pocco-Objekte, aber ich kann nicht garantieren, dass es in jedem komplexen Fall funktioniert. Es erfolgt jedoch ein Ereignis für nicht geerbte Klassen, wenn die Eigenschaften übereinstimmen.

using Newtonsoft.Json;

namespace CastParentToChild
{
    public class Program
    {
        public static void Main(string[] args)
        {
            var p = new parent();
            p.a=111;
            var s = JsonConvert.SerializeObject(p);
            var c1 = JsonConvert.DeserializeObject<child1>(s);
            var c2 = JsonConvert.DeserializeObject<child2>(s);

            var foreigner = JsonConvert.DeserializeObject<NoFamily>(s);

            bool allWorks = p.a == c1.a && p.a == c2.a && p.a == foreigner.a;
            //Your code goes here
            Console.WriteLine("Is convertable: "+allWorks + c2.b);
        }
    }

    public class parent{
        public int a;
    }

    public class child1 : parent{
     public int b=12345;   
    }

    public class child2 : child1{
    }

    public class NoFamily{
        public int a;
        public int b = 99999;
    }

    // Is not Deserializeable because
    // Error 'NoFamily2' does not contain a definition for 'a' and no extension method 'a' accepting a first argument of type 'NoFamily2' could be found (are you missing a using directive or an Assembly reference?)
    public class NoFamily2{
        public int b;
    }
}
0
Reiner

Es scheint natürlich, dass das Basisobjekt einen Konstruktor mit Parametern für seine Eigenschaften hat:

public class MyObject 
{
    public MyObject(prop1, prop2, ...)
    {
        this.Prop1 = prop1;
        this.Prop2 = prop2;
    }
}

Dann können Sie in Ihrem Nachkommenobjekt Folgendes haben:

public MyObjectSearch(MyObject obj)
    :base(obj.Prop1, obj.Prop2)

Dadurch werden Doppelarbeiten im Zusammenhang mit Zuweisungen reduziert. Sie können Reflection verwenden, um alle Eigenschaften automatisch zu kopieren. Dies scheint jedoch lesbarer zu sein. 

Wenn Ihre Klassen über so viele Eigenschaften verfügen, dass Sie über das Automatisieren des Kopierens der Eigenschaften nachdenken, verstoßen Sie wahrscheinlich gegen das Prinzip der Einzelverantwortung , und Sie sollten eher überlegen, Ihr Design zu ändern.

0
BartoszKP

Es gibt Bibliotheken, um dies zu handhaben. Wenn Sie jedoch nur eine schnelle Implementierung an einigen Stellen wünschen, würde ich auf jeden Fall einen "Kopierkonstruktor" wählen, wie zuvor vorgeschlagen.

Ein interessanter Punkt, der nicht erwähnt wird, ist, dass ein Objekt, wenn es eine Unterklasse ist, auf die privaten Variablen des Kindes vom Elternteil aus zugreifen kann!

Fügen Sie auf dem übergeordneten Element eine CloneIntoChild-Methode hinzu. In meinem Beispiel:

  • Order ist die übergeordnete Klasse
  • OrderSnapshot ist die untergeordnete Klasse
  • _bestPrice ist ein nicht lesbares privates Mitglied in Order. Aber Order kann es für OrderSnapshot setzen.

Beispiel:

public OrderSnapshot CloneIntoChild()
{
    OrderSnapshot sn = new OrderSnapshot()
    {
        _bestPrice = this._bestPrice,
        _closed = this._closed,
        _opened = this._opened,
        _state = this._state       
    };
    return sn;
}

ANMERKUNG: Readonly-Membervariablen MÜSSEN im Konstruktor festgelegt werden. Daher müssen Sie den untergeordneten Konstruktor verwenden, um diese ...

Obwohl ich nicht generell "Up-Sizing" mag, verwende ich diesen Ansatz häufig für analytische Momentaufnahmen ... 

0
James Joyce