2009-05-08 17 views
5

それはのために渡します。.NET不変オブジェクト

public class Entity 
{ 
    private int ID1; 
    public int ID 
    { 
     get { return ID1; } 
    } 
} 

が、これのためにされていません。

public class Entity 
{ 
    public int ID { get; private set; } 
} 

ここでは「WTF?」という質問になります。

答えて

3

他の場所に投稿された回答への小さな変更。保護されたパブリックセッターを持つプロパティが少なくとも1つ存在する場合、次の値は0以外を返します。 GetSetMethodがIsPublic(publicのみ)ではなくnull(セッターなし)とIsPrivate(つまりパブリックではないか保護されている)のテストを返すかどうかを確認します。

var setterCount = 
      (from s in typeof(Entity).GetProperties(
       BindingFlags.Public 
       | BindingFlags.NonPublic 
       | BindingFlags.Instance) 
      where 
       s.GetSetMethod(true) != null // setter available 
       && (!s.GetSetMethod(true).IsPrivate) 
      select s).Count(); 

はそれにもかかわらず、pointed out in Daniel Brückner's answerように、クラスはない公に可視性セッターを有することが必要であるが、クラスのない十分条件は不変と見なされるべきです。

+0

CanWrite == trueはsetterの存在を保証するため、テストs.GetSetMethod(true)!= nullが余分です。 –

+0

true、 "s.GetSetMethod(true)!= null"はCanWriteが真であることと等価であるため、そのうちの1つは冗長です。 IsPrivateプロパティを逆参照する前に、nullをテストすることについて明示的にしたいので、CanWriteを削除しました。 – Joe

1

確かに、あなたのprivate setは2番目のものです。

最初に、クラスEntityのインスタンスは、外部クラスによってID1プロパティが書き込まれることがあります。後者で

は、セッターは、クラス自体にプライベートですので、唯一の(つまりはそれ自身のコンストラクタ/初期化子)は

が第二に、あなたのセッターのうちprivateを取り、それが必要エンティティ内から呼び出すことができますpass

+0

ID1はバッキングフィールドであり、ファーストクラスのプロパティではなく、ゲッターを介して外部に読み込まれます。外部のクラスからどのようにアクセスできるのですか? –

+0

彼の元の例では、彼は最初のプロパティのパブリックセッターを持っていた。彼は以来、質問を編集しています。 http://stackoverflow.com/revisions/839066/list –

+0

私は参照してください。ありがとうEoin。 –

2

私はCanWriteが、セッターがあれば真を返すと言います。プライベートセッターもセッターです。

パブリックセッターがあるため、初回通過時に少し驚いています。私がまだカフェインで低すぎない限り、設定数は1であるため、アサーションは失敗するはずです。 CanWriteは両方に対してtrueを返すだけなので、両方とも失敗するはずです。 (そして、linqクエリはIDを含むpublicプロパティをpublicとして取得するだけです)

(編集)これで、ファーストクラスのコードが変更されたので、もうsetterがありません。

これは、CanWriteがsetterメソッドのアクセサを見ていることを前提としていますが、そうではありません。なぜなら公共ゲッターの - - プロパティが公開されていることを、

var setterCount = 
      (from s in typeof (Entity).GetProperties(BindingFlags.Public | BindingFlags.Instance) 
      where s.GetSetMethod().IsPublic 
      select s) 
       .Count(); 
+0

私はちょうどコードを試したことがあり、私は同意します。私は両方のテストクラスのために1セッターのカウントを取得します。 –

+0

私がカフェインで低すぎない限り、最初のケースではセッターが表示されません。 ID1はメンバ変数でありプロパティではないので、 "セッタ"メソッドはありません。 –

+0

。 代わりにTSが行うべきことは、propertyinfoでGetSetMethod()を呼び出し、返されたMethodInfoでIsPublicが真であるかどうかをチェックすることです。 –

7

問題があり、それは書き込み可能である - なぜなら、民間セッター:あなたが行う必要があります。あなたはあなたのテストを洗練させる必要があります。

さらに、メソッド内のプライベートデータを変更できるため、このように不変性を保証することはできません。不変性を保証するためには、すべてのフィールドが読み取り専用で宣言され、自動実装されたプロパティがないことを確認する必要があります。あなたが簡単なタイプ彼らの不変のため

typeof(MyType).IsImmutable() 

とインスタンス

myInstance.IsImmutable() 

をテストすることができ、この拡張メソッドで

public static Boolean IsImmutable(this Type type) 
{ 
    const BindingFlags flags = BindingFlags.Instance | 
           BindingFlags.NonPublic | 
           BindingFlags.Public; 

    return type.GetFields(flags).All(f => f.IsInitOnly); 
} 

public static Boolean IsImmutable(this Object @object) 
{ 
    return (@object == null) || @object.GetType().IsImmutable(); 
} 

。インスタンスフィールドを見ると

注意

  • あなたが書き込み可能な性質を持つことができますが、今変更することができフィールドがあることを保証します。
  • 自動実装されたプロパティは、非公開の匿名のバッキングフィールドのために予測されるように、不変性テストに失敗します。
  • リフレクトを使用してreadonlyフィールドを変更することはできます。
  • おそらく、このオブジェクトが状態に属しているため、すべてのフィールドのタイプが不変であることを確認する必要があります。
  • サイクルの可能性があり、基本タイプが変更可能であるため、これはすべてのフィールドに対して単純なFiledInfo.FieldType.IsImmutable()で行うことはできません。
+0

'object'と' bool'の代わりに 'Object'と' Boolean'を使う理由はありますか? –

+0

コンパイラが文字列、bool、int、オブジェクト、およびその他すべてのものに対して生成するものに依存しない(コンパイラには選択肢がないことがわかりますが) )。 –

+0

これには十分な理由がありますか? –

3

あなたはおそらく、セット方法と、GETメソッドは、同じアクセス修飾子を持っていないので、あなたはPropertyInfo自体に頼ることができない

propertyInfo.GetSetMethod().IsPublic 

を呼び出す必要があります。

var setterCount = 
     (from s in typeof (Entity).GetProperties(
      BindingFlags.Public 
      | BindingFlags.NonPublic 
      | BindingFlags.Instance) 
     where 
      s.GetSetMethod() != null  // public setter available 
     select s) 
+0

+1これはほぼあります。しかしGetSetMethod()は、プライベートセッターやセッターのないプロパティに対してはnullを返します。これを変更してnullをテストする必要があります。 – Joe

+0

@Joe:OK、CanWriteはもう必要ありません –

+0

ProPrtyInfo.GetSetMethod()は、ProeprtyInfoを呼び出さない限り、public setメソッドのみを返すため、s.GetSetMethod()。IsPublicは不要です。 GetSetMethod(Boolean nonPublic)。 –

1

私が正しく理解していれば、エンティティは不変になります。 もしそうなら、テストは

var setterCount = (from s in typeof(string).GetProperties(BindingFlags.Public | BindingFlags.Instance).Select(p => p.GetSetMethod()) 
    where s != null && s.IsPublic 
    select s).Count(); 

Assert.That(setterCount == 0, Is.True, "Immutable rule is broken"); 
+0

アサーションが正しい - setterCount == 0が真であり、このテストが失敗した場合、アサーションに "不変ルールが壊れている"というメッセージが表示される場合、タイプは不変です。 –

+0

固定、ありがとうDaniel – SeeR

2

に変更する必要があります私はにプロパティ条件の変更をお勧め:

s.CanWrite && s.GetSetMethod().IsPublic 
0

は、あなたがそのCanWriteプロパティ真実が何であるかの特性を見るために、それをデバッグしようとしたことがありあなたのLINQクエリによって返されていますか?どうやら、それはあなたが期待していないことを拾っています。その知識があれば、LINQクエリをより選択的にして、必要な方法で作業することができます。 また、@Daniel Brucknerは、フィールドも読み取り専用でない限り、クラスが完全に可変ではないことに同意します。呼び出し元はメソッドを呼び出したり、getterを介して読み取り専用として公開されているプラ​​イベートフィールドを内部的に変更するgetterを使用することがあります。これにより、不変ルールが破られ、クライアントが驚くことになり、問題が発生します。変更可能なオブジェクトの操作は、副作用を持ってはいけません。