2013-08-09 9 views
12

私は華麗だとレコードが自動的に余分な労力でIStructuralEqualityを実装しているという事実は、私はこの中に望むなりC#からF#のコピーと更新機能にアクセスできますか?我々は</p> <pre><code>type MyRecord = { X: int; Y: int; Z: int } let myRecord1 = { X = 1; Y = 2; Z = 3; } </code></pre> <p>を定義することができますし、それを更新するために、F#で例えば

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

を行うことができますC#。しかし、おそらく私はF#で自分のレコードを定義することはできますが、それでもC#でいくつかの更新を行うことができます。私は

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

のようなAPIは、方法があると想像し、私は上記のようにCopyAndUpdateを実装するために、汚いハックを気にしませんか? CopyAndUpdateのためのC#のsignitureは

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

おそらくこれは役に立ちます:http://v2matveev.blogspot.com/2010/05/copy-and-update-in-c.html – desco

+0

[C#:duplicate of extension methodを "with "F#で?](http://stackoverflow.com/questions/6832880/c-how-to-define-an-extension-method-as-with-in-f) –

答えて

7

それは行うことができますされますが、適切にそれを行うことは非常に困難になるだろう(と、それは間違いなく私の答えには適合しません)。以下の単純な実装では、あなたのオブジェクトが読み書きをプロパティとパラメータなしのコンストラクタのみを持っていることを前提としています

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

あなたはおそらく不変タイプでこれを使用したいと思うので、これは少し、ポイントを破る - しかし、あなたは常に持っていますすべての引数を使ってコンストラクタを呼び出すことができます。インスタンスを作成するときに、コンストラクタパラメータを読み込むことができるプロパティでリンクする方法は不明です。

With方法は、新しいインスタンスを作成し、コピーし、すべてのプロパティ値と、その後、変更したい1セット(式ツリーから抽出されたPropertyInfoを使用して - !すべてのチェックを行わずに)

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

以下予想通りデモは動作しますが、私が言ったように、それは限界がたくさんあります。私は、suコマンドではないかもしれませんここWithのような普遍的な反射ベースの方法を使用して考えて、一般的には

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

をあなたがそれを適切に実装するために多くの時間を持っていない限り、良いアイデアです。任意のパラメータをとり、その値がnullでない場合は、クローン値(手作業で作成)に設定する、使用するすべてのタイプに対して1つのWithメソッドを実装するほうが簡単です。あなたはオプションの引数を使用して似たような達成できる

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

私はレコードを定義することを望んでいる私はIStructuralEqualityとIComparableを無料で入手します。それは私が手動でC#で実装する必要がありますバグ。 – bradgonesurfing

+0

私はいくつかのF#レコードであなたのコードを試し、それがうまくいくかどうかを見ていきます。 – bradgonesurfing

+0

偉大な仕事トーマスしかし、C#では、それは遠く離れてmyRecord2 = {myRecord1 Y = 100; Z = 2} – phillip

6

::署名のようなものだろう

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

これは実際にF#がレコードを生成するコードにかなり近いです。

+0

残念ですが、目的は、IStructualEqualityとIComparableを自動的に実装するため、F#レコードを使用することです。私はC#からF#レコードを使いたいです。おそらく大きな質問をしますが、私はすべての定型文を記入するのに疲れています。 – bradgonesurfing

+0

申し訳ありません。私はあなたがC#でレコードを複製しようとしていると思った。誰かが興味がある場合に備えて私の答えを残しておきます。 – Daniel

+1

これは賢明な答え(私の+1)だと思います。オプションの引数を取る 'With'メソッドは、F#レコードに拡張メソッドとして追加することができます(もっと書いていますが、より速く、より良く動作します:-)) –

関連する問題

 関連する問題