24

ユーザーがプロパティグリッドを介して編集できるようにするさまざまなパブリックプロパティを持つクラスがあります。永続性のために、このクラスは、DataContractSerializerを介してXMLファイルとの間でシリアル化/逆シリアル化されます。

クラスのインスタンスに加えた変更をユーザーが保存(シリアル化)できるようにしたい場合があります。それでも、ユーザーが変更を保存できるようにしたくない場合は、代わりにプロパティグリッド内のすべてのプロパティを読み取り専用として表示する必要があります。ユーザーが後で保存できないような変更を加えられるようにしたくありません。MS Wordで、ユーザーが現在他のユーザーによって開かれているドキュメントを、読み取り専用としてのみ開くことができるようにする方法と同様です。

私のクラスには、クラスを読み取り専用にするかどうかを決定するブールプロパティがありますが、このプロパティを使用して、実行時にクラスプロパティに読み取り専用属性を動的に追加することはできますか?そうでない場合、代替ソリューションは何ですか?クラスを読み取り専用のラッパークラスでラップする必要がありますか?

4

7 に答える 7

28

不変性は、C#にはまだ改善の余地がある領域です。プロパティを使用して単純な不変の型を作成するreadonlyことは可能ですが、型がいつ変更可能であるかをより高度に制御する必要があると、障害にぶつかり始めます。

読み取り専用の動作を「強制」する必要があるかどうかに応じて、次の3つの選択肢があります。

  1. タイプで読み取り専用フラグを使用し(実行しているように)、呼び出し元がタイプのプロパティを変更しようとしないようにします。書き込みが試行された場合は、例外をスローします。

  2. 読み取り専用インターフェースを作成し、タイプにそれを実装させます。このようにして、そのインターフェイスを介して、読み取りのみを実行する必要があるコードに型を渡すことができます。

  3. タイプを集約し、読み取り操作のみを公開するラッパークラスを作成します。

最初のオプションは、既存のコードのリファクタリングが少なくて済むという点で最も簡単ですが、型の作成者がインスタンスが不変である場合と不変である場合に消費者に通知する機会が最も少なくなります。このオプションはまた、不適切な使用を検出する際のコンパイラからのサポートを最小限に抑え、エラー検出をランタイムに委任します。

2番目のオプションは、リファクタリングの労力をあまりかけずにインターフェイスの実装が可能であるため、便利です。残念ながら、呼び出し元は基になる型にキャストして、それに対して書き込もうとすることができます。多くの場合、このオプションは、不変性が侵害されないようにするために読み取り専用フラグと組み合わされます。

3番目のオプションは、施行に関しては最も強力ですが、コードの重複が発生する可能性があり、リファクタリングの作業になります。多くの場合、オプション2と3を組み合わせて、読み取り専用ラッパーと可変タイプの多態性の関係を作成すると便利です。

個人的には、不変性を強制する必要があると予想される新しいコードを作成するときに、3番目のオプションを好む傾向があります。 不変のラッパーを「キャストアウェイ」することは不可能であり、すべてのセッターに厄介なif-read-only-throw-exceptionチェックを書き込むことを回避できることが多いという事実が気に入っています。

于 2010-04-27T20:29:27.747 に答える
3

ライブラリを作成している場合は、プライベート/内部クラスを使用してパブリックインターフェイスを定義できます。読み取り専用クラスのインスタンスを外部コンシューマーに返す必要があるメソッドは、代わりに読み取り専用インターフェイスのインスタンスを返す必要があります。現在、具体的なタイプへのダウンキャストは、そのタイプが公開されていないため不可能です。

ユーティリティライブラリ

public interface IReadOnlyClass
{
    string SomeProperty { get; }
    int Foo();
}
public interface IMutableClass
{
    string SomeProperty { set; }
    void Foo( int arg );
}

あなたの図書館

internal MyReadOnlyClass : IReadOnlyClass, IMutableClass
{
    public string SomeProperty { get; set; }
    public int Foo()
    {
        return 4; // chosen by fair dice roll
                  // guaranteed to be random
    }
    public void Foo( int arg )
    {
        this.SomeProperty = arg.ToString();
    }
}
public SomeClass
{
    private MyThing = new MyReadOnlyClass();

    public IReadOnlyClass GetThing 
    { 
        get 
        { 
            return MyThing as IReadOnlyClass;
        }
    }
    public IMutableClass GetATotallyDifferentThing
    {
        get
        {
            return MyThing as IMutableClass
        }
    }
}

これで、を使用する人は誰でも、SomeClass2つの異なるオブジェクトのように見えるものを取り戻すことができます。もちろん、リフレクションを使用して基になるタイプを確認することもできます。これにより、これは実際には同じタイプの同じオブジェクトであることがわかります。ただし、そのタイプの定義は、外部ライブラリではプライベートです。この時点で、まだ技術的に定義を取得することは可能ですが、それを実行するにはヘビーウィザードリーが必要です。

プロジェクトによっては、上記のライブラリを1つにまとめることができます。それを妨げるものは何もありません。権限を制限するDLLに上記のコードを含めないでください。

コメントはXKCDの功績によるものです。

于 2018-02-23T18:00:09.390 に答える
2

なぜ次のようなものではないのですか?

private int someValue;
public int SomeValue
{
    get
    {
         return someValue;
    }
    set
    {
         if(ReadOnly)
              throw new InvalidOperationException("Object is readonly");
         someValue= value;
    }
于 2010-04-27T20:20:06.527 に答える
1

すべてを読み取り専用に保つラッパークラスを使用します。これは、スケーラビリティ、信頼性、および一般的な読みやすさのためです。

私は、上記の3つの利点に加えて、さらに何かを提供する、これを行う他の方法を予測していませ。私の意見では、ここでラッパークラスを使用することは間違いありません。

于 2010-04-27T20:19:38.557 に答える
0

このようなものが役立ちますか?

class Class1
{
    private bool _isReadOnly;

    private int _property1;
    public int Property1
    {
        get
        {
            return _property1;
        }
        set
        {
            if (_isReadOnly) 
              throw new Exception("At the moment this is ready only property.");
            _property1 = value;
        }
    }
}

プロパティを設定するときに例外をキャッチする必要があります。

これがあなたが探しているものであることを願っています。

于 2010-04-27T20:21:48.893 に答える
0

readonly実行時にプロパティを読み取り専用に変更しても、コンパイル時のチェック(キーワードで指定されたものなど)を取得することはできません。したがって、手動でチェックして例外をスローする以外に方法はありません。

しかし、おそらく、クラスへのアクセスを再設計する方が良いでしょう。たとえば、「ライタークラス」を作成します。これは、基になる「データクラス」が現在書き込み可能かどうかをチェックします。

于 2010-04-27T20:26:32.100 に答える
0

PostSharpを使用して、_readOnlyがtrueに設定されたときにどのフィールドにも新しい値を渡さないOnFieldAccessAspectを作成できます。アスペクトコードを使用すると、繰り返しがなくなり、フィールドを忘れることはありません。

于 2010-04-27T21:29:20.177 に答える