2013-08-09 6 views
12

Zum Beispiel in F # können wirKönnen wir von C# auf die Funktion zum Kopieren und Aktualisieren von F # zugreifen?

type MyRecord = { 
    X: int; 
    Y: int; 
    Z: int 
    } 

let myRecord1 = { X = 1; Y = 2; Z = 3; } 

definieren und aktualisieren ich

tun können
let myRecord2 = { myRecord1 with Y = 100; Z = 2 } 

Das ist brillant und die Tatsache, dass die Datensätze implementieren automatisch IStructuralEquality ohne zusätzlichen Aufwand macht mich für diese wünschen C#. Aber vielleicht kann ich meine Datensätze in F # definieren, aber trotzdem einige Updates in C# durchführen. Ich stelle mir eine API wie

MyRecord myRecord2 = myRecord 
    .CopyAndUpdate(p=>p.Y, 10) 
    .CopyAndUpdate(p=>p.Z, 2) 

Gibt es eine Möglichkeit, und ich kümmere mich nicht schmutzig Hacks, CopyAndUpdate wie oben zu implementieren? Die C# signiture für CopyAndUpdate würde

T CopyAndUpdate<T,P> 
    (this T 
    , Expression<Func<T,P>> selector 
    , P value 
    ) 
+1

Vielleicht wird dies nützlich sein: http://v2matveev.blogspot.com/2010/05/copy-and-update-in-c.html – desco

+0

möglich duplicate von [C#: wie definiert man eine Erweiterungsmethode als "mit "in F #?] (http://stackoverflow.com/questions/6832880/c-how-to-define-an-extension-metho-as-with-in-f) –

Antwort

7

ist Es kann getan werden, aber das richtig tun wird ziemlich schwer sein (und es wird auf jeden Fall nicht in meiner Antwort passen). Die folgende einfache Implementierung wird davon ausgegangen, dass Ihr Objekt Eigenschaften nur Lese-Schreib-und parameterlose Konstruktor:

class Person 
{ 
    public string Name { get; set; } 
    public int Age { get; set; } 
} 

Dieser leicht den Punkt besiegt, weil Sie wahrscheinlich diese verwenden, auf unveränderliche Typen wollen - aber dann immer Sie haben um den Konstruktor mit allen Argumenten aufzurufen, und es ist nicht klar, wie die Konstruktorparameter (wenn Sie eine Instanz erstellen) mit den Eigenschaften verknüpft werden, die Sie lesen können.

Die With Methode erstellt eine neue Instanz, kopiert alle Werte Eigenschaft und setzt dann das eine, die Sie ändern möchten (die PropertyInfo extrahiert aus dem Ausdrucksbaum mit - ohne Prüfung)

public static T With<T, P>(this T self, Expression<Func<T, P>> selector, P newValue) 
{ 
    var me = (MemberExpression)selector.Body; 
    var changedProp = (System.Reflection.PropertyInfo)me.Member; 

    var clone = Activator.CreateInstance<T>(); 
    foreach (var prop in typeof(T).GetProperties()) 
    prop.SetValue(clone, prop.GetValue(self)); 

    changedProp.SetValue(clone, newValue); 
    return clone; 
} 

Folgende Demo verhält sich wie erwartet, aber wie gesagt, es hat viele Einschränkungen:

var person = new Person() { Name = "Tomas", Age = 1 }; 
var newPerson = person.With(p => p.Age, 20); 

Im allgemeinen ich denke, eine universelle Reflexion basierende Methode verwendet wie With hier vielleicht nicht su ch eine gute Idee, es sei denn, Sie haben viel Zeit, um es richtig zu implementieren. Es ist möglicherweise einfacher, für jeden verwendeten Typ eine Methode With zu implementieren, die optionale Parameter verwendet und ihre Werte auf einen (manuell erstellten) geklonten Wert setzt, wenn der Wert nicht null ist. Die Unterschrift wäre so etwas wie:

public Person With(string name=null, int? age=null) { ... } 
+0

Ich hoffe, den Rekord zu definieren F # so bekomme ich IStructuralEquality und IComparable kostenlos. Es Bugs ich muss das manuell in C# implementieren. – bradgonesurfing

+0

Ich werde Ihren Code mit einigen F # Records ausprobieren und sehen, ob es funktioniert. – bradgonesurfing

+0

toller Job Tomas, aber in C# es fällt weit unter myRecord2 = {myRecord1 mit Y = 100; Z = 2} – phillip

6

Sie etwas erreichen könnten ähnliche optionale Argumente mit:

class MyRecord { 
    public readonly int X; 
    public readonly int Y; 
    public readonly int Z; 
    public MyRecord(int x, int y, int z) { 
     X = x; Y = y; Z = z; 
    } 
    public MyRecord(MyRecord prototype, int? x = null, int? y = null, int? z = null) 
     : this(x ?? prototype.X, y ?? prototype.Y, z ?? prototype.Z) { } 
} 

var rec1 = new MyRecord(1, 2, 3); 
var rec2 = new MyRecord(rec1, y: 100, z: 2); 

Das ist eigentlich ziemlich nah an dem Code, den F # für Datensätze erzeugt.

+0

Es tut uns leid, aber das Ziel ist es, F # Datensätze zu verwenden, da sie IStructuralEquality und IComparable automatisch implementieren. Ich möchte die F # Datensätze von C# verwenden. Große Frage vielleicht, aber ich habe es satt, die ganze Platte auszufüllen. – bradgonesurfing

+0

Entschuldigung. Ich dachte, du versuchst, Datensätze in C# zu replizieren. Ich werde meine Antwort hinterlassen, falls jemand interessiert ist. – Daniel

+1

Ich denke, das ist eine vernünftige Antwort (meine +1). Eine 'With' Methode mit optionalen Argumenten könnte als Erweiterung zu einem F # Record hinzugefügt werden (mehr schreiben, aber es wird schneller und besser funktionieren :-)) –

Verwandte Themen