6

クラスを不変にしたい。明らかな方法は、すべてのフィールドを as として宣言しget; private set;、コンストラクターですべてのフィールドを初期化することです。したがって、クライアントはコンストラクターですべてを提供する必要があります。問題は、各フィールドにラベルがないため、コンストラクターでそれらを渡すフィールドが 10 個以上ある場合、非常に判読不能になることです。

たとえば、これはかなり読みやすいです:

info = new StockInfo
        {
            Name = data[0] as string,
            Status = s,
            LotSize = (int)data[1],
            ISIN = data[2] as string,
            MinStep = (decimal)data[3]
        };

これと比較してください:

new StockInfo(data[0] as string, s, (int) data[1], data[2] as string, (decimal) data[3])

そして今、私は10個以上のパラメータを持っていることをイメージしています.

では、クラスを不変にして可読性を確保するにはどうすればよいでしょうか?

コンストラクターを使用する場合は、同じフォーマットのみを使用することをお勧めします。

info = new StockInfo(
            data[0] as string,           // Name
            s,                           // Status
            (int)data[1],                // LotSize
            data[2] as string,           // ISIN
            (decimal)data[3]             // MinStep
       );

もっと良いものを提案できますか?

4

6 に答える 6

8

C# の名前付きパラメーターを使用して、これを行う方法は次のとおりです。

var info = new StockInfo
(
    Name: data[0] as string,
    Status: s,
    LotSize: (int)data[1],
    ISIN: data[2] as string,
    MinStep: (decimal)data[3]
);
于 2012-08-14T15:22:21.160 に答える
8

ここにいくつかのオプションがあります。あなたに最適なものを決定する必要があります。

読みやすくするために、名前付き引数を持つ古典的な不変オブジェクト (大規模なコンストラクターを持つ) を使用します。(欠点: 多くのコンストラクター引数を持つことを嫌う人もいます。名前付き引数をサポートしていない他の .NET 言語から使用するには不便かもしれません。)

info = new StockInfo
           (
             name: data[0] as string,
             status: s,
             ...
           )

不変のインターフェイスを介して可変オブジェクトを公開します。(欠点: オブジェクトはまだキャストで変更される可能性があります。書き込む余分な型です。)

public interface IStockInfo
{
   string Name { get; }
   string Status { get; }
}

IStockInfo info = new StockInfo
                      {
                          Name = data[0] as string,
                          Status = s,
                          ...
                      }

変更可能なオブジェクトの読み取り専用ビューReadOnlyCollection<T>を公開します -例を参照してください。(欠点: 実装する余分な型。余分なオブジェクトが作成されます。余分な間接化。)

var readOnlyInfo = new ReadOnlyStockInfoDecorator(info);

可変オブジェクトの不変のクローンを公開します。(欠点: 実装する余分な型。余分なオブジェクトが作成されます。コピーが必要です。)

var immutableInfo = new ImmutableStockInfo(info);

フリーズ可能なオブジェクトを使用してください。(欠点: フリーズ後のミューテーション試行は、実行時までキャッチされません。)

info.Freeze();
info.Name = "Test"; // Make this throw an exception.

流暢なスタイルのビルダーなどを使用します (欠点: パターンに慣れていない人もいるかもしれません。書く余分なコードがたくさんあります。たくさんのコピーが作成されます。中間状態は違法かもしれません)。

info = StockInfo.FromName(data[0] as string)
                .WithStatus(s) // Make this create a modified copy
                .WithXXX() ;
于 2012-08-14T15:32:43.240 に答える
6

いいえ、できません。不変オブジェクトがあるか、オブジェクトを変更する機能が必要です。

  1. 名前付きパラメーターを使用できます。
  2. 他のオブジェクト (およびグループ パラメーター) を渡すことを検討して、1 つのオブジェクトに何らかの形で非常に類似したパラメーターのみが含まれるようにすることができます。

コードを見て、最初にパラメーターを抽出することをお勧めします。そのため、使用data[0] as stringするようなものを渡す代わりに、. これにより、コードが読みやすくなります。string stockName = data[0] as string;stockName

オブジェクトのコンストラクターに非常に多くのパラメーターを渡す場合は、設計を修正することをお勧めします。単一責任の原則に違反している可能性があります。

于 2012-08-14T15:21:40.300 に答える
1

では、クラスを不変にして可読性を確保するにはどうすればよいでしょうか?

名前付きパラメータを使用できます:

info = new StockInfo(
        name: data[0] as string,
        status: s,              
        lotSize: (int)data[1],  
        isin: data[2] as string,
        minStep: (decimal)data[3]
   );

オブジェクト初期化子を使用する目的は可読性ではないことに注意してください。また、オブジェクト初期化子はコンストラクターの代わりと見なされるべきではありません。型を適切に初期化するために必要なすべてのパラメーターを常にコンストラクターに含めることをお勧めします。不変型は、コンストラクターまたはファクトリ メソッドを介して、構築中にすべての引数を渡す必要があります。

オブジェクト初期化子は、コンストラクターの後に値を設定することで機能するため、不変型では機能しません。

于 2012-08-14T15:23:26.927 に答える
0

いくつかの可能な解決策:

慣例による不変性

オブジェクトは変更可能です。適切に動作するだけで、セットアップ後に変更することはありません。これは、ほとんどの用途 (オブジェクトが公開されている場合) にはまったく不適切ですが、使用される場所の数が限られている (したがって、使用できる場所の数が限られている) 内部の「ワーカー」オブジェクトに対してはうまく機能します。めちゃくちゃにして変更します)。

より深い階層

実際のクラスに 5 つ以上のフィールドがあると仮定すると (特にツールチップを備えた IDE を使用している場合は、それほど読みにくくはありません)、一部は構成可能である可能性があります。たとえば、同じクラスに名前、住所、緯度と経度の異なる部分がある場合、それを名前、住所、座標のクラスに分割できます。

そのような場合に発生するボーナスは、そのようなオブジェクトが多数ある場合 (そして、これが価値があるために多くのことを意味します。数千未満のものは時間の無駄です)、そのようなオブジェクトがいくつかあり、それらの間に同一のフィールドがいくつかある場合です。それらの共有値が異なる同一のオブジェクトではなく、それぞれの場合に同じオブジェクトを持つようにそれらを構築することができます - 結局のところ不変であるため、エイリアシングでうまくいかない可能性のあるすべてのことは起こり得ません。

ビルダークラス

例はStringBuilderUriBuilderです。ここにまさにあなたが抱えている問題があります。不変性の利点が必要ですが、オブジェクトを複数のステップで構築できるようにしたい場合が少なくともいくつかあります。

したがって、同等のプロパティを持つ別の変更可能なクラスを作成しますが、セッターとゲッター、および他の変更メソッド (Append()クラスによって意味があるかどうかはもちろん異なります) と、不変クラスのインスタンスを構築するメソッドを使用します。 .

コンストラクターが 30 個ものパラメーターを持つクラスでこれを利用しました。これは、同じ懸念事項の一部である 30 個の異なる情報が実際にあったためです。この場合、コンストラクターを呼び出す唯一の場所は、対応するビルダー クラスでした。

于 2012-08-14T16:30:50.633 に答える
0

AsMutableAsNewMutable、およびAsImmutableメソッドを含む「変更可能」オブジェクトのインターフェースを用意すると役立つ場合があることをお勧めします。不変オブジェクトはAsImmutable、それ自体を返すだけで要素を実装できます。AsImmutable可変オブジェクトは、可変オブジェクトをコンストラクターのパラメーターとして使用して作成された新しい不変オブジェクトを返すか、同等であることがわかっている不変オブジェクトを返すことによって実装する必要があります。不変オブジェクトのコンストラクターは、元のオブジェクトの内容を含む新しいオブジェクトをロードする必要がありますが、AsImmutableすべての変更可能なフィールドを呼び出します。

AsMutable不変オブジェクトは、新しい「浅く」可変オブジェクトを構築する必要があります。コードが「maybe-mutable」プロパティによって参照されるオブジェクトを変更したい場合は、プロパティをAsMutable同等のものに設定する必要があります。

不変オブジェクトでメソッドを呼び出すと、のAsNewMutableように動作する必要がありますAsMutable。変更可能なオブジェクトで呼び出すと、 と同等に動作するかAsImmutable.AsMutable、ネストされた変更可能なオブジェクトの変更可能なクローンが作成される可能性があります (変更されるネストされたオブジェクトによっては、どちらの方法が適している場合もあります)。

このパターンを使用すると、不変オブジェクト (最も顕著なのは、「ディープ」コピーを作成する必要なく「ディープ」オブジェクトのスナップショットを取得する機能) と可変オブジェクトの両方の利点の多くを享受できるはずです。もの (同じインスタンスで多くのステップを実行してオブジェクトを生成できること)。それぞれの可変オブジェクトが、ある時点で自身の状態と同一であった不変オブジェクトへの参照を保持することにより、パフォーマンスが向上する場合があります。不変のインスタンスを構築した後、オブジェクトは他のインスタンスと一致するかどうかを確認し、一致する場合は新しいインスタンスを破棄して古いインスタンスを返します。これは余分な作業を表しているように見えますが、実際には、ミュータブル オブジェクトがAsImmutableミューテーション間で 2 回以上呼び出されるシナリオでは、パフォーマンスが大幅に向上する可能性があります。1 つが呼び出す場合AsImmutableツリーのほとんどが実際には変更されていない深いツリー構造で 2 回、ツリーの変更されていない部分が同じオブジェクト インスタンスを 2 回とも返すと、将来の比較が容易になります。

注: このパターンを使用する場合は、深く不変な型のGetHashCodeandをオーバーライドする必要がありますが、変更可能な型はオーバーライドしないでください。Equals同一の値を保持する不変オブジェクトは交換可能であり、同等であると見なす必要がありますが、可変オブジェクトは、その値に関係なく、それ自体以外のものと同等であってはなりません。doubleまた、オブジェクトが型、float、またはのいずれかを保持している場合Decimalは、これらの型がオーバーライドObject.Equalsされて等価以外のものを意味するため、注意が必要になる場合があることにも注意してください。

于 2012-08-14T17:05:36.817 に答える