2013-08-09 4 views
12

Per esempio in F # possiamo definirePossiamo accedere alla funzione di copia e aggiornamento F # da C#?

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

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

e di aggiornarlo posso fare

let myRecord2 = { myRecord1 with Y = 100; Z = 2 } 

Questo è brillante e il fatto che i documenti implementano automaticamente IStructuralEquality senza sforzi aggiuntivi mi fa desiderare per questo nel C#. Tuttavia, forse posso definire i miei record in F #, ma sono comunque in grado di eseguire alcuni aggiornamenti in C#. Immagino un'API come

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

C'è un modo, e non mi dispiace hack sporco, per implementare CopyAndUpdate come sopra? Il C# signiture per CopyAndUpdate sarebbe

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

Forse questo sarà utile: http://v2matveev.blogspot.com/2010/05/copy-and-update-in-c.html – desco

+0

possibile duplicato di [C#: come definire un metodo di estensione come "con "in F #?] (http://stackoverflow.com/questions/6832880/c-how-to-define-an-extension-method-as-with-in-f) –

risposta

7

Si può fare, ma facendo che correttamente sta per essere molto difficile (e sicuramente non entra nella mia risposta). Il seguente semplice implementazione presuppone che l'oggetto è solo lettura-scrittura proprietà e dei parametri-less costruttore:

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

Questo sconfigge un po 'il punto, perché si sarebbe probabilmente desidera utilizzare questo su tipi immutabili - ma poi si deve sempre chiamare il costruttore con tutti gli argomenti e non è chiaro come collegare i parametri del costruttore (quando si crea un'istanza) con le proprietà che si possono leggere.

Il metodo With crea una nuova istanza, copia tutti i valori delle proprietà e quindi imposta quella che si desidera modificare (utilizzando il PropertyInfo estratto dalla pianta di espressione - senza alcun controllo)

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

Il seguente demo si comporta come previsto, ma come ho detto, ha un sacco di limitazioni:

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

In generale, penso che utilizzando un metodo di riflessione a base universale come With qui potrebbe non essere su ch una buona idea, a meno che tu non abbia molto tempo per implementarlo correttamente. Potrebbe essere più semplice implementare un solo metodo With per ogni tipo che utilizza i parametri opzionali e imposta i loro valori su un valore clonato (creato a mano) se il valore non è null. La firma sarebbe qualcosa di simile:

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

Spero di definire il record in F # quindi ottengo IStructuralEquality e IComparable gratuitamente. Mi dà fastidio doverlo implementare manualmente in C#. – bradgonesurfing

+0

Proverò il tuo codice con alcuni record F # e vedrò se funziona. – bradgonesurfing

+0

ottimo lavoro Tomas ma in C# è molto meno di myRecord2 = {myRecord1 con Y = 100; Z = 2} – phillip

6

si potrebbe ottenere qualcosa di simile utilizzando argomenti opzionali:

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

Questo è in realtà molto vicino al codice che genera F # per i record.

+0

Ci scusiamo ma l'obiettivo è utilizzare i record F # mentre implementano IStructualEquality e IComparable automaticamente. Voglio usare i record F # da C#. Grande chiedo forse, ma sono stanco di compilare tutto il boilerplate. – bradgonesurfing

+0

Siamo spiacenti. Ho pensato che stavi cercando di replicare i record in C#. Lascerò la mia risposta nel caso qualcuno fosse interessato. – Daniel

+1

Penso che questa sia una risposta sensata (il mio +1). Un metodo 'With' che accetta argomenti facoltativi potrebbe essere aggiunto come metodo di estensione a un record F # (più scrittura, ma funzionerà più velocemente e meglio :-)) –

Problemi correlati