2013-08-09 13 views
12

Na przykład w F # możemy zdefiniowaćCzy możemy uzyskać dostęp do funkcji kopiowania i aktualizacji F # z C#?

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

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

i go zaktualizować mogę zrobić

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

To genialny i fakt, że zapisy automatycznie wdrożyć IStructuralEquality bez dodatkowego wysiłku sprawia mi życzyć to w DO#. Być może jednak mogę zdefiniować moje rekordy w F #, ale nadal mogę wykonać pewne aktualizacje w C#. Wyobrażam API jak

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

Czy istnieje sposób, a ja nie mam nic przeciwko brudne hacki, aby wdrożyć CopyAndUpdate jak wyżej? C# signiture dla CopyAndUpdate byłoby

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

Może to będzie przydatne: http://v2matveev.blogspot.com/2010/05/copy-and-update-in-c.html – desco

+0

możliwy duplikat [C#: jak zdefiniować metodę rozszerzenia jako "z "in F #?" (http://stackoverflow.com/questions/6832880/c-how-to-define-an-extension-method-as-with-in-f) –

Odpowiedz

7

To można zrobić, ale robi to właściwie ma być dość trudne (i to na pewno nie zmieści się w mojej odpowiedzi). Poniższa prosta realizacja zakłada, że ​​obiekt jest tylko do odczytu i zapisu właściwości i parametrów mniej konstruktora:

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

To nieznacznie pokonał punkt, ponieważ prawdopodobnie chcesz użyć tego na niezmiennych typów - ale wtedy zawsze masz wywołanie konstruktora ze wszystkimi argumentami i nie jest jasne, jak połączyć parametry konstruktora (podczas tworzenia instancji) z właściwościami, które można odczytać.

Sposób With tworzy nową instancję, kopiuje wszystkie wartości nieruchomości, a następnie ustawia ten, który chcesz zmienić (za pomocą PropertyInfo wyciąg z drzewa wyrażenie - bez jakiejkolwiek kontroli)

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

Poniższy demo zachowuje się zgodnie z oczekiwaniami, ale jak powiedziałem, to ma wiele ograniczeń:

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

Generalnie myślę, że stosując metodę uniwersalną do refleksji opartej jak With tutaj może nie być su Dobry pomysł, chyba że masz dużo czasu na jego prawidłowe wdrożenie. Łatwiej byłoby po prostu zaimplementować jedną metodę dla każdego używanego typu, który pobiera opcjonalne parametry i ustawia ich wartości na sklonowaną wartość (utworzoną ręcznie), jeśli wartość nie jest równa null. Podpis byłoby coś jak:

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

Mam nadzieję, że zdefiniuję rekord w F #, więc dostaję IStructuralEquality i IComparable za darmo. To powoduje, że muszę to zaimplementować ręcznie w C#. – bradgonesurfing

+0

Wypróbuję twój kod z niektórymi rekordami F # i zobaczę, czy to działa. – bradgonesurfing

+0

świetna robota Tomas, ale w C# jest znacznie niższa od myRecord2 = {myRecord1 z Y = 100; Z = 2} – phillip

6

Można osiągnąć coś podobnego używając argumentów opcjonalnych:

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

To jest rzeczywiście bardzo blisko do kodu, który generuje F # dla rekordów.

+0

Przepraszam, ale celem jest użycie rekordów F #, które automatycznie implementują IStructualEquality i IComparable. Chcę użyć rekordów F # z C#. Duże pytanie może, ale jestem już zmęczony wypełnianiem wszystkich szablonów. – bradgonesurfing

+0

Przepraszamy. Myślałem, że próbujesz replikować rekordy w C#. Zostawię odpowiedź na wypadek, gdyby ktoś był zainteresowany. – Daniel

+1

Myślę, że jest to rozsądna odpowiedź (moje +1). Metoda 'With' z opcjonalnymi argumentami może być dodana jako metoda rozszerzenia do rekordu F # (więcej pisania, ale będzie działać szybciej i lepiej :-)) –

Powiązane problemy