web-dev-qa-db-ja.com

C#で不変オブジェクトを作成する方法

C#パターン検証のベストプラクティス に関する質問では、最高得票数の回答は次のとおりです。

私はすべての検証をコンストラクターで実行する傾向があります。ほとんどの場合、不変オブジェクトを作成するため、これは必須です。

C#で不変オブジェクトを正確にどのように作成しますか? readonlyキーワードを使用しますか?

Entity Frameworkで生成されたモデルクラスのコンストラクターで検証する場合、これはどのように正確に機能しますか?

以下のようになりますか?

public partial readonly Person
{
    public Person()
}
31
delete

ここで興味深い質問は、コメントからのあなたの質問です。

ある時点で値を変更する必要のないオブジェクトはどのようなものでしょうか?モデルクラスではないと思いますよね?データベース内の人の名前を変更する必要がありました-これはこの考えには合いません。

さて、すでに不変のものを検討してください。数値は不変です。 12になると、12になります。変更することはできません。 12を含む変数がある場合、変数の内容を13に変更できますが、変更するのは数値12ではなく変数です。

文字列と同じです。 「abc」は「abc」であり、変更されることはありません。 「abc」を含む変数がある場合、「abcd」に変更できますが、「abc」は変更されず、変数が変更されます。

リストはどうですか? {12、 "abc"}は、12の後に "abc"が続くリストであり、そのリストは変更されません。リスト{12、 "abcd"}は別のリストです。

そして、それは物事がRailsから離れるところです。 C#ではどちらの方法でも実行できるためです。 IDを変更せずにリストの内容を変更できる場合、これら2つのリストの間にreferential identityがあると言えます。

「モデル」について話すとき、あなたは頭の上の爪を右に打ちました。変化するものをモデリングしていますか?その場合は、変化するタイプでモデル化するのが賢明です。その利点は、モデルの特性がモデル化されるシステムと一致することです。欠点は、変更を「元に戻す」「ロールバック」機能などを実行することが非常に難しくなることです。

つまり、{12、 "abc"}を{12、 "abcd"}に変異させてから、その変異をロールバックしたい場合、どうすればよいでしょうか。リストが不変の場合は、両方の値を保持し、どちらを「現在の」値にするかを選択するだけです。リストが変更可能な場合は、変更を元に戻す方法を認識している「元に戻す機能」をアンドゥロジックに保持させる必要があります。

具体的な例として、不変のデータベースを作成できます。不変データベースの誰かの名前をどのように変更しますか?あなたはしません。必要なデータを含むnewデータベースを作成します。不変タイプのコツは、数十億バイトをコピーせずに効率的に行うことです。不変のデータ構造設計では、ほぼ同一の2つの構造間で状態を共有するための巧妙な方法を見つける必要があります。

68
Eric Lippert

すべてのフィールドを読み取り専用として宣言することは、不変オブジェクトを作成するための良いステップですが、これだけでは十分ではありません。これは、読み取り専用フィールドが変更可能なオブジェクトへの参照である可能性があるためです。

C#では、不変性はコンパイラーによって強制されません。注意する必要があるだけです。

11
Mark Byers

この質問には2つの側面があります。

  1. オブジェクトをインスタンス化するときの不変タイプ
  2. EFがオブジェクトをインスタンス化するときの不変タイプ

最初の側面はこのような構造を要求します:

public class MyClass
{
  private readonly string _myString;
  public string MyString
  {
    get
    {
      return _myString;
    }
  }

  public MyClass(string myString)
  {
    // do some validation here

    _myString = myString;
  }
}

今問題-EF。 EFにはパラメーターのないコンストラクターが必要であり、EFにはプロパティにセッターが必要です。私は非常によく似た 質問はこちら に尋ねました。

タイプは次のようになります。

public class MyClass
{
  private string _myString;
  public string MyString
  {
    get
    {
      return _myString;
    }
    private set
    {
      _myString = value;
    }
  }

  public MyClass(string myString)
  {
    // do some validation here

    _myString = myString;
  }

  // Not sure if you can change accessibility of constructor - I can try it later
  public MyClass()
  {}
}

MyStringプロパティのプライベートセッターについてもEFに通知する必要があります。これは、EDMXファイルのエンティティのプロパティで構成されます。 EFがDBからオブジェクトを実体化する場合、検証はありません。また、ObjectContext.CreateObjectなどのメソッドを使用できなくなります(オブジェクトを埋めることはできません)。

Entity Object T4テンプレートとデフォルトのコード生成は、パラメーターを持つコンストラクターではなく、ファクトリーメソッドCreateMyClassを作成します。 POCO T4テンプレートはファクトリメソッドを生成しません。

私はこれをEFコードで最初に試しませんでした。

6
Ladislav Mrnka
6
Alexei Levenkov

不変値オブジェクトは、変更できない値オブジェクトです。その状態は変更できません。新しいものを作成する必要があります

Eric Lippertのブログをご覧ください。

不変性の種類 http://blogs.msdn.com/b/ericlippert/archive/2007/11/13/immutability-in-c-part-one-kinds-of-immutability.aspx

見て

C#の不変オブジェクトパターン-どう思いますか?

2
ukhardy

Entity Frameworkで生成されたモデルクラスのコンストラクターで検証する場合、これはどのように正確に機能しますか?

EFはエンティティクラスのプロパティをパブリックにする必要があるため、このコンテキストでは機能しません。それ以外の場合はインスタンス化できません。

ただし、コード内でさらに不変オブジェクトを使用することもできます。

1
user151323

@Eric Lippert良いコメントですが、質問への回答に加えて:

ある時点で値を変更する必要がないのは、どのようなオブジェクトでしょうか。モデルクラスではないと思いますよね?データベース内の人の名前を変更する必要がありました-これはこのアイデアには合いません。

大規模なデータ構造があり、その情報をクエリしたいとしますが、それは常に変化しています。誰かがある場所から別の場所に何かを預けているときに、システムの合計を数えようとしないようにするために、何らかのロックシステムが必要です。 (倉庫管理システムと言います)

これらのことは常に予期しない方法で影響を及ぼし、データが足元で変化するため、これを行うのは困難です。

更新していないときに大きなデータ構造をフリーズして、メモリを変更できず、一貫した状態で一時停止できるとしたらどうでしょう。ここでもう一度変更したい場合は、データ構造を新しい場所にコピーする必要があります。これはかなり大きいため、欠点ですが、データの新しいコピーは共有されないため、何もロックする必要はありません。更新されるまで。つまり、誰でもいつでもデータ構造の最新のコピーを読み取って、複雑なことを行うことができます。

ですから、同時実行の問題に対処するのが嫌いで、処理するデータが多すぎない場合は、非常に便利な概念です。 (たとえば、1MBのデータで10 /秒の更新の場合、10MBのデータがコピーされます)

0
Chris Kuliukas