1

私は現在、どこかで見たばかりのものを理解するのに苦労しています。

2つのクラスがあるとしましょう:

 class MyFirstCLass{
      public int membVar1;
      private int membVar2;
      public string membVar3;
      private string membVar4; 

      public MyFirstClass(){
      }
 }

と :

 class MySecondClass{
      private MyFirstClass firstClassObject = new MyFirstClass();

      public MyFirstClass FirstClassObject{
           get{
                return firstClassObject;
           }
      }
 }

私がこのようなことをすると:

 var secondClassObject = new MySecondClass(){
      FirstClassObject = {membVar1 = 42, membVar3 = "foo"}
 };

secondClass は MySecondClass のインスタンス化であり、readOnly プロパティを持つ MyFirstClass 型のプライベート メンバ変数が 1 つあります。ただし、me​​mbVar1 と membVar2 の状態を変更することはできます。カプセル化の問題はありませんか?

よろしくお願いします、

Al_th

4

3 に答える 3

2

MySecondClass の FirstClassObject プロパティにセッターがないという事実は、ゲッターから返されたオブジェクトが不変になることを意味しません。public フィールドがあるため、これらのフィールドは変更可能です。したがって、 と言うのは完全に合法secondClassObject.FirstClassObject.membVar1 = 42です。setter がないということは、 firstClassObject フィールドに格納されているオブジェクト参照を別のオブジェクトへの参照に置き換えることができないことを意味するだけです。

于 2012-08-21T09:42:30.317 に答える
1

注意:の値は変更していませんMySecondClass.FirstClassObject。そのプロパティ内の値を変更するだけです。

次の 2 つのスニペットを比較してください。FirstClassObject1 つ目は有効ですが、2 つ目はプロパティに新しい値を代入しようとするためではありません。

// legal:
var secondClassObject = new MySecondClass(){ 
  FirstClassObject = {membVar1 = 42, membVar3 = "foo"} }

// won't compile:
// Property or indexer 'FirstClassObject' cannot be assigned to -- it is read only
var secondClassObject = new MySecondClass(){ 
  FirstClassObject = new MyFirstClass {membVar1 = 42, membVar3 = "foo"} }

基本的に、あなたのコードはこれを書くための非常に凝った方法です:

var secondClassObject = new MySecondClass();
secondClassObject.FirstClassObject.membVar1 = 42;
secondClassObject.FirstClassObject.membVar3 = "foo";

そして、それが私がそれを書く方法です。明快で分かりやすいです。

于 2012-08-21T09:46:30.380 に答える
0

タイプ の保存場所も、タイプMyFirstCLassのプロパティによって返される値も、MyFirstCLassフィールドmembVar1membVar2などを含みません。代わりに、保存場所またはプロパティには、 のインスタンスを識別MyFirstCLassするか、それが「null」であることを示すのに十分な情報が含まれています。一部の言語またはフレームワークでは、オブジェクトを識別する参照型が存在しますが、特定の操作のみを実行できますが、Java と .NET はどちらも無差別オブジェクト参照を使用します。オブジェクトが参照を保持する外部コードで何かを実行できる場合参照を取得する外部コードはそれを行うことができます。

クラスが自身の状態をカプセル化するために変更可能なオブジェクトを使用しており、外部の世界がその状態を参照できるようにしたいが、外部の世界がそれを改ざんすることを許可したくない場合は、オブジェクトを外部のコードに直接返してはならず、代わりに外部コードは別のものです。可能性は次のとおりです。

  • オブジェクトに含まれる状態のすべての側面を個別に公開します (たとえばmembVar1、カプセル化されたオブジェクトの値を返すプロパティを持ちますmembVar1)。これにより混乱を避けることができますが、呼び出し元はプロパティをグループとして処理できなくなります。

  • プライベート オブジェクトへの参照を保持し、読み取り要求 (書き込み要求ではない) をそれらのメンバーに転送するメンバーを持つ読み取り専用ラッパーの新しいインスタンスを返します。返されたオブジェクトは読み取り専用の「ビュー」として機能しますが、外部コードには、そのような 2 つのオブジェクトが同じ基になるオブジェクトのビューであるかどうかを識別する適切な方法がありません。

  • コンストラクターで初期化される読み取り専用ラッパー型のフィールドを持ち、それを返すプロパティを持ちます。各オブジェクトに読み取り専用ラッパーが 1 つしか関連付けられていない場合、2 つのラッパー参照は、同じラッパーを識別している場合にのみ、同じラップされたオブジェクトを表示します。

  • おそらく、新しい可変コピーを作成し、それに新しい読み取り専用ラッパーを返すことによって、基になるデータの不変コピーを作成します。これにより、ライブの「ビュー」ではなく、データの「スナップショット」が発信者に提供されます。

  • 基になるデータの新しい変更可能なコピーを作成し、それを返します。これには、コピーを変更して基になるデータを変更しようとする呼び出し元が警告なしでコピーを変更できるが、操作が機能しないという欠点があります。変更可能な構造体が「悪」である理由のすべての議論は、ここで二重に適用されます。公開されたフィールド構造を受け取るコードは、受け取った構造への変更がそれが由来するソースに影響を与えないことを期待する必要がありますが、変更可能なクラスを受け取るコードはオブジェクトはそれを知る方法がありません。プロパティはこのように動作すべきではありません。このような振る舞いは、一般に、その意図を明確にするメソッドにのみ適しています (例:FirstClassObjectAsNewMyFirstClass();

  • 呼び出し元が、基になるデータを受け入れることができる型の可変オブジェクトを渡し、データをそれにコピーする必要があります。これにより、呼び出し元に変更可能な形式でデータが提供されますが (場合によっては、操作が簡単になる場合もあります)、同時に、オブジェクトの "所有者" に関する混乱を避けることができます。追加のボーナスとして、呼び出し元が多くのクエリを作成する場合、呼び出し元はそれらすべてに対して同じ可変オブジェクトを再利用できるため、不要なオブジェクトの割り当てを回避できます。

  • データを構造内にカプセル化し、プロパティが構造を返すようにします。このような使用法に躊躇する人もいるかもしれませんが、呼び出し元がデータを断片的に変更したい場合には便利な規則です。このアプローチは、問題のデータが離散値の固定セット (四角形の座標と寸法など) に制限されている場合にのみ実際に機能しますが、呼び出し元が .NET 構造とは何か ( as all . NET プログラマは、セマンティクスが本質的に明白であるべきです。

これらの選択肢のうち、最後の 2 つだけが、型システムを介して、呼び出し元が期待するセマンティクスを明確にします。呼び出し元から変更可能なオブジェクトを受け入れると、明確なセマンティクスが提供されますが、使用法が扱いにくくなります。公開されたフィールド構造を返すと、明確なセマンティクスが提供されますが、データが離散値の固定セットで構成されている場合に限ります。データの変更可能なコピーを返すと便利な場合もありますが、メソッド名が何をしているかが明確な場合にのみ適切です。他の選択肢では、一般に、データがスナップショットを表しているのかライブの「ビュー」を表しているのかという問題が曖昧になります。

于 2013-12-29T17:33:50.830 に答える