2016-05-12 10 views
1

Ich habe zwei Klassen mit Eigentum Getter nurWie kann ich Reflektionen verwenden, um Eigenschaften mit fehlenden Sätzen zu ändern?

public class A 
{ 
    public A(string name) 
    { 
     Name = name; 
    } 
    public string Name { get; } 
    public string Value { get; set;} 
    public string Data { get; set;} 
} 

public class B 
{ 
    public B(string name) 
    { 
     Name = name; 
    } 
    public string Name { get; } 
    public string Value { get; set;} 
} 

Sie in Form unterschiedlich sind, aber einige der gleichen Eigenschaftsnamen und Typen teilen. Wie kann ich Werte kopieren, wenn sie nur Getter haben?

Dies ist ein typisches Szenario, wenn ich ein Objekt als Konstruktorparameter zum Extrahieren von Werten aus dem neuen Objekt sende. Dann muss ich die Werte eins nach dem anderen kopieren. Dies kann viel Code produzieren und ist schwer zu pflegen.

Kann dies vereinfacht werden? Gibt es eine Möglichkeit, Reflektionen zum Kopieren von Objekten zu verwenden, wenn das Ziel nur Getter-Eigenschaften hat?

+0

Was ist der Punkt der Auto-Eigenschaften ohne sogar private Setter automatisch implementiert? Wie sollte ein solches Objekt ohne Reflektionsmagie verwendet werden? –

+0

Es ist in der neuesten Version von C# erlaubt, aber es kann nur im Konstruktor festgelegt werden. Schreibgeschützt, wenn erstellt. – Frode

+0

stimme zu, aber Sie setzen nur 'Name' im Konstruktor. Es gibt also keine ordinale Möglichkeit, 'Value' und' Data' einzurichten. –

Antwort

0

Es ist möglich, eine Hilfsfunktion zu erstellen, die zwei Objekte kopiert, wobei das Zielobjekt nur Eigenschaften-Getter hat.

Versuchen Sie dies;

public static void CopyItem<U, T>(U source, T target) 
{ 
    // Need a way to rename the backing-field name to the property Name ("<A>k__BackingField" => "A") 
    Func<string, string> renameBackingField = key => new string(key.Skip(1).Take(key.IndexOf('>') - 1).ToArray()); 

    // Get public source properties (change BindingFlags if you need to copy private memebers as well) 
    var sourceProperties = source.GetType().GetProperties().ToDictionary(item => item.Name); 
    // Get "missing" property setter's backing field 
    var targetFields = typeof(T).GetFields(BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.SetField).ToDictionary(item => renameBackingField(item.Name)); 

    // Copy properties where target name matches the source property name 
    foreach(var sourceProperty in sourceProperties) 
    { 
     if (targetFields.ContainsKey(sourceProperty.Key) == false) 
      continue; // No match. skip 

     var sourceValue = sourceProperty.Value.GetValue(source); 
     targetFields[sourceProperty.Key].SetValue(target, sourceValue); 
    } 
} 

Dies ist eine allgemeine Beschreibung. Wahrscheinlich möchten Sie auch die Eigenschaften der Eigenschaft und des Felds überprüfen, bevor Sie den Wert kopieren, um Ausnahmen zu vermeiden. Name und Datentyp sollten übereinstimmen.

Verwenden Sie CopyItem, um übereinstimmende Quelleigenschaften in einen Konstruktor zu kopieren;

public class SomeClass 
{ 
    public SomeClass(SomeSourceClass source) 
    { 
     Helper.CopyItem(source, this); 
    } 
} 
+0

Sind Sie sicher, dass dies funktioniert? Was ist wenn "T" mehr als ein privates Feld enthält? In diesem Fall gibt Ihre Hilfsmethode mehr als einmal eine leere Zeichenfolge zurück, die zu einer doppelten Schlüsselausnahme führt. Vielleicht sollte es eine 'where'-Klausel geben, die nach unterstützenden Feldern filtert. BTW: Es sieht so aus, als würde diese Lösung auch nach dem Ausführen des Konstruktors funktionieren (kann nicht getestet werden). Wenn das stimmt, werden Sie Eigenschaften ändern, die unveränderlich sein sollten. Und zuletzt: Leistung nicht vergessen. –

+0

Dies deckt wahrscheinlich nicht alle Szenarien ab. Wie wenn Sie automatische Eigenschaften mit Gettern nur mit benutzerdefinierten Hintergrundfeldern mischen. Dann können Sie natürlich einige Überraschungen haben. Vor allem, wenn Ihre Backing-Felder andere Namen als Ihre Eigenschaften haben. Aber in den meisten Fällen würde das funktionieren. Ich habe einige Zeit damit verbracht, einen Weg zu finden, Eigenschaften nur mit Gettern zu setzen. Wollte nur teilen :) – Frode