12

Bar参照タイプを含むプライベートフィールドを持つクラスがありますFoo。公共の財産で公開したいFooのですが、財産の消費者が変更できるようにしたくありませんFoo...ただし、内部で変更可能である必要があります。Barつまり、フィールドを作成できませんreadonly

だから私が欲しいのは:

      private _Foo;

      public Foo 
      { 
         get { return readonly _Foo; } 
      } 

...もちろん無効です。のクローンを返すこともできますがFoo(それがそうであると仮定してIClonable)、これは消費者には明らかではありません。プロパティの名前をFooCopy??に変更する必要があります。GetCopyOfFoo代わりにそれは方法であるべきですか?ベストプラクティスは何だと思いますか?ありがとう!

4

8 に答える 8

12

C++の「const」に相当するものを求めているようです。これはC#には存在しません。コンシューマーがオブジェクトのプロパティを変更できないことを示す方法はありませんが、他の何かが変更できます(もちろん、変更するメンバーがパブリックであると仮定します)。

提案されたようにFooのクローンを返すか、ReadOnlyCollectionがコレクションに対して行うようにFooのビューを返すことができます。もちろん、不変の型を作ることができれば、それは人生をより簡単にするでしょう...Foo

フィールドを読み取り専用にすることと、オブジェクト自体を不変にすることには大きな違いがあることに注意してください。

現在、タイプ自体が両方の方法で状況を変える可能性があります。それはすることができます:

_Foo = new Foo(...);

また

_Foo.SomeProperty = newValue;

2番目の処理のみが必要な場合、フィールドは読み取り専用である可能性がありますが、プロパティをフェッチするユーザーがオブジェクトを変更できるという問題があります。最初に行う必要があるだけで、実際にFooはすでに不変であるか、不変にすることができる場合は、「ゲッター」のみを持つプロパティを提供するだけで問題ありません。

フィールドの値を変更する(別のインスタンスを参照するようにする)ことと、フィールドが参照するオブジェクトの内容を変更することの違いを理解することは非常に重要です。

于 2009-03-25T12:12:36.150 に答える
3

残念ながら、現時点では C# でこれを回避する簡単な方法はありません。インターフェイスで の「読み取り専用部分」を抽出しFoo、プロパティが の代わりにそれを返すようにすることができますFoo

于 2009-03-25T12:18:23.717 に答える
2

すでに推測しているように、コピーを作成する、、、ReadOnlyCollectionまたはFoo不変にすることは、通常、3つの最良のルートです。

関係する作業の量によっては、基になるフィールドを単に返すよりも重要なことを行う場合は常に、プロパティではなくメソッドを好むことがあります。メソッドは、何かが起こっていることを意味し、消費者がAPIを使用しているときにフラグを立てます。

于 2009-03-25T12:14:03.667 に答える
1

誰にもあなたの状態をいじられたくないなら...それを公開しないでください! 他の人が言ったように、何かがあなたの内部状態を表示する必要がある場合は、それの不変の表現を提供してください。または、自分で何かをするのではなく、クライアントに何かをするように言ってもらいます( Google は「聞かないでください」を意味します)。

于 2009-03-25T12:40:42.383 に答える
1

受信して返す Foo オブジェクトを「複製」することは、防御的コピーと呼ばれる通常の方法です。ユーザーに表示される目に見えないクローン作成の副作用がない限り、これを実行しない理由はまったくありません。多くの場合、クラスの内部プライベート データを保護する唯一の方法です。特に、C++ の概念がconst利用できない C# や Java ではそうです。(IE、これら 2 つの言語で真に不変なオブジェクトを適切に作成するには、これを行う必要があります。)

明確にするために、考えられる副作用は、ユーザーが(合理的に)元のオブジェクトが返されることを期待していたり​​、正しく複製されない Foo によって保持されているリソースのようなものです。(その場合、IClonable を実装して何をしているのですか?!)

于 2009-03-25T12:16:19.637 に答える
0

Jon Skeet のコメントを明確にするために、変更可能な Foo の不変のラッパー クラスであるビューを作成できます。次に例を示します。

class Foo{
 public string A{get; set;}
 public string B{get; set;}
 //...
}

class ReadOnlyFoo{
  Foo foo; 
  public string A { get { return foo.A; }}
  public string B { get { return foo.B; }}
}
于 2009-03-25T12:25:11.110 に答える
0

C# で C++ const の動作を実際に再現できます。手動で行うだけです。

いずれにせよFoo、呼び出し元がその状態を変更できる唯一の方法は、メソッドを呼び出すか、プロパティを設定することです。

たとえば、Fooタイプは次のFooClassとおりです。

class FooClass
{
    public void MutateMyStateYouBadBoy() { ... }

    public string Message
    {
        get { ... }
        set { ... }
    }
}

したがって、あなたの場合、彼らがMessageプロパティを取得することを喜んでいますが、それを設定することはできません。また、彼らがそのメソッドを呼び出すことに満足していないことは間違いありません。

したがって、彼らができることを説明するインターフェイスを定義します。

interface IFooConst
{
    public string Message
    {
        get { ... }
    }
}

mutating メソッドを省略し、プロパティの getter のみを残しました。

次に、そのインターフェイスを のベース リストに追加しますFooClass

Fooプロパティを持つクラスに、フィールドがあります。

private FooClass _foo;

プロパティのゲッター:

public IFooConst Foo
{
    get { return _foo; }
}

これは基本的に、C++constキーワードが自動的に行うことを正確に手動で再現します。疑似 C++ の用語では、型の参照は、メンバーとしてマークされたconst Foo &メンバーのみを含む、自動生成された型のようなものです。これを C# の理論的な将来のバージョンに変換すると、次のように宣言できます。FooconstFooClass

class FooClass
{
    public void MutateMyStateYouBadBoy() { ... }

    public string Message
    {
        get const { ... }
        set { ... }
    }
}

私が実際に行ったことは、1 つの安全なメンバーに新しいキーワードをタグ付けするIFooConstことによって、 の情報を にマージしたことだけです。したがって、ある意味では、 const キーワードを追加しても、このパターンへの正式なアプローチを除いて、言語にはあまり追加されません。FooClassconst

次にconst、オブジェクトへの参照があるFooClass場合:

const FooClass f = GetMeAFooClass();

の const メンバーのみを呼び出すことができますf

FooClass定義が公開されている場合、呼び出し元は を にキャストできることに注意してIFooConstくださいFooClass。しかし、彼らは C++ でもそれを行うことができます - それは「キャストアウェイconst」と呼ばれ、 と呼ばれる特別な演算子を伴いconst_cast<T>(const T &)ます。

また、製品のバージョン間でインターフェイスを簡単に進化させることができないという問題もあります。あなたが定義したインターフェイスをサード パーティが実装する可能性がある場合 (サード パーティがそれを見ることができれば自由に実行できます)、将来のバージョンで新しいメソッドを追加することはできません。他のユーザーがコードを再コンパイルする必要はありません。しかし、それは、他の人が構築できる拡張可能なライブラリを作成している場合にのみ問題になります。ビルトインconst機能がこの問題を解決するかもしれません。

于 2009-03-25T13:01:04.927 に答える
0

私は同様のセキュリティについて考えていました。おそらく方法があります。非常に明確ですが、短くはありません。一般的な考え方は非常に単純です。ただし、私は常にいくつかの方法を見つけたので、テストしたことはありません。しかし、あなたはそれをチェックすることができます-多分それはあなたのために働くでしょう.

これは疑似コードですが、その背後にあるアイデアが明確であることを願っています

public delegate void OnlyRuller(string s1, string s2);
public delegate void RullerCoronation(OnlyRuller d);

class Foo {
  private Foo();
  public Foo(RullerCoronation followMyOrders) {
     followMyOrders(SetMe);
  }

  private SetMe(string whatToSet, string whitWhatValue) {
     //lot of unclear but private code
  }
}

したがって、このプロパティを作成するクラスでは SetMe メソッドにアクセスできますが、まだプライベートであるため、作成者 Foo を除いて変更不可に見えます。

それでも、いくつかのプロパティよりも大きなものについては、これはおそらくすぐに非常に混乱するでしょう。そのため、私は常に他のカプセル化方法を好みました。ただし、クライアントが Foo を変更できないようにすることが非常に重要な場合は、これが 1 つの代替手段です。

しかし、私が言ったように、これは理論にすぎません。

于 2015-02-11T22:39:40.187 に答える