12

ドメイン モデルには不変の値オブジェクトがたくさんあります。その一例が、緯度、経度、高さによって定義される位置です。

/// <remarks>When I grow up I want to be an F# record.</remarks>
public class Position
{
    public double Latitude
    {
        get;
        private set;
    }

    // snip

    public Position(double latitude, double longitude, double height)
    {
        Latitude = latitude;
        // snip
    }
}

位置の編集を許可する明白な方法は、getterとsetterを持つ ViewModel と、検証済みの不変の位置インスタンスを抽出する ToPosition() メソッドを構築することです。この解決策は問題ありませんが、多くの重複コード、特に XAML が発生します。

問題の値オブジェクトは、通常、X、Y、Z およびいくつかの補助要素のバリアントである 3 ~ 5 つのプロパティで構成されます。これを考慮して、さまざまな可能性を処理するために 3 つの ViewModel を作成することを検討しました。各 ViewModel は、各プロパティの値のプロパティと、各ラベル (「Latitude」など) に表示する説明を公開する必要があります。

さらに進むと、N 個のプロパティを処理し、リフレクションを使用してすべてを接続できる 1 つの一般的な ViewModel に単純化できるようです。プロパティ グリッドのようなものですが、不変オブジェクト用です。プロパティ グリッドの 1 つの問題は、次のようなラベルとテキスト ボックスを使用できるように、外観を変更できるようにすることです。

Latitude:   [      32 ]  <- TextBox
Longitude:  [     115 ]
Height:     [      12 ]

または、次のような DataGrid に配置します。

Latitude  |  Longitude  |  Height
      32           115         12

だから私の質問は:

この問題を解決するエレガントな方法を思いつきますか? これを行うライブラリや、同様の記事はありますか?

私は主に探しています:

  • コードの重複を最小限に抑える
  • 新しい値のオブジェクト タイプを簡単に追加できます
  • ある種の検証で拡張可能
4

3 に答える 3

5

カスタム型記述子を使用して、この問題を解決できます。Position にバインドする前に、型記述子を使用して get メソッドと set メソッドを提供し、値を一時的に構築することができます。変更がコミットされると、不変オブジェクトが構築される可能性があります。

次のようになります。

DataContext = new Mutable(position, 
    dictionary => new Position(dictionary["lattitude"], ...)
);

バインディングは次のようになります。

<TextBox Text="{Binding Path=Lattitude}" />

Mutable オブジェクトは、その TypeDescriptor のおかげで、Lattitude のようなプロパティを持つように「ふりをする」ためです。

または、バインディングでコンバーターを使用して、何らかの規則を考え出すこともできます。

Mutable クラスは現在の不変オブジェクトを取得し、Func<IDictionary, object>編集が完了したら新しい不変オブジェクトを作成できるようにします。Mutable クラスは型記述子を利用し、設定時に新しい不変オブジェクトを作成する PropertyDescriptor を作成します。

型記述子の使用例については、次を参照してください。

http://www.paulstovell.com/editable-object-adapter

編集: 不変オブジェクトの作成頻度を制限したい場合は、Mutable でも実装できる BindingGroups と IEditableObject も参照してください。

于 2009-11-05T06:58:16.883 に答える
2

同じ状況で考えられる選択肢を調査しているときに、この古い質問を見つけました。他の誰かがつまずいた場合に備えて、更新する必要があると考えました。

もう 1 つのオプション (.Net 4 がまだリリースされていないため、Paul がソリューションを提供したときには利用できませんでした) は、同じ戦略を使用することですが、CustomTypeDescriptors を使用して実装する代わりに、ジェネリック、動的オブジェクト、およびリフレクションを組み合わせて使用​​して同じ効果を実現します。 .

この場合、クラスを定義します

class Mutable<ImmutableType> : DynamicObject
{
   //...
}

コンストラクターは、ポールの答えと同じように、不変型のインスタンスと、その新しいインスタンスを辞書から構築するデリゲートを取ります。ただし、ここでの違いは、TryGetMember と TrySetMember をオーバーライドして、コンストラクター デリゲートの引数として最終的に使用する内部辞書を作成することです。リフレクションを使用して、受け入れている唯一のプロパティが ImmutableType で実際に実装されているものであることを確認します。

パフォーマンスに関しては、Paul の回答の方が高速であり、C# 開発者を苦しめることが知られている動的オブジェクトを含まないことに賭けます。しかし、このソリューションの実装も少し単純です。なぜなら、型記述子は少し難解だからです。


要求された概念実証/実装例は次のとおりです。

https://bitbucket.org/jwrush/mutable-generic-example

于 2013-08-17T02:49:22.547 に答える
0

この問題を解決するエレガントな方法を思いつきますか?

正直なところ、あなたは問題の周りを踊っているだけですが、問題自体については言及していません ;)。

あなたの問題を正しく推測できれば、MultiBinding と IMultiValueConverter の組み合わせでうまくいくはずです。

HTH。

PS ところで、値オブジェクトではなく、不変のクラス インスタンスがあります。値オブジェクト (structキーワードで記述されます) を使用すると、セッターが存在するかどうかに関係なく、より多くのことを行うことができます :)。

于 2009-11-03T05:17:33.977 に答える