[Effective C#] 項目23 クラス内のオブジェクトの参照を返さないようにすること

本日の Effective C# は「項目23 クラス内のオブジェクトの参照を返さないようにすること」です。

これ大事なんですけどね。守りたいんですが、ついつい面倒で…。

public class Foo
{
    public int Value { get; set; }
}
 
public class Sample
{
    private Foo _sampleFoo = new Foo();
 
    public Foo SampleFoo
    {
        get { return _sampleFoo; }
    }
}

こんなコードがあったときに、readonly で SampleFoo として公開しているので、_sampleFoo は変更されたくないんですが。

var sample = new Sample();
 
// これはコンパイルエラー。想定通り。
sample.SampleFoo = new Foo();
 
// これは動作するが、意図しない動作。
sample.SampleFoo.Value = 1;
 
// これも動作するが、意図しない動作。
var foo = sample.SampleFoo;
foo.Value = 2;

このように、インスタンスは変更できないが、インスタンスの値そのものが変更できてしまいます。これを防ぐためには、

public interface IReadOnlyFoo
{
    int Value { get; }
}
 
public class Foo : IReadOnlyFoo
{
    public int Value { get; set; }
}
 
public class Sample
{
    private Foo _sampleFoo = new Foo();
 
    public IReadOnlyFoo SampleFoo
    {
        get { return _sampleFoo; }
    }
}

などとします。まあ、余計に interface を書かなければいけないので、結構面倒なんですよね。まあ、ReSharper の助けを借りればすぐなのですが。

ちなみに、値型の場合はこのような心配はありません。

public class Foo
{
    public Point Point { get; set; }
}

と書いてあるとき、

var foo = new Foo();
 
// コンパイルエラー。
foo.Point.X = 1;
 
// 動作はするが、コピーを変更しているため、foo.Point の値は変化しない。
var point = foo.Point;
point.X = 2;

となるので、readonly プロパティであれば、値を変更することは不可能です。

実は前者がコンパイルエラーになるのをはじめて知りました。

さて、書籍の方に戻って。DataSet (というか DataTable) を readonly で公開する方法が書かれています。

public class MyBusinessObject
{
    // private データメンバにアクセスする読み取り専用プロパティ
    private DataSet _ds;
    public IList this[string tableName]
    {
        get {
            DataView view = _ds.DefaultViewManager.CreateDataView(_ds.Tables[tableName];
            view.AllowNew = false;
            view.AllowDelete = false;
            view.AllowEdit = false;
            return new;
        }
    }
}

このように、IList を用いて公開しています。また、行の追加・削除・編集を禁止しているところも興味深いです。(この書籍は C# 1.1 向けなのであれですが、実際のコードではジェネリックを使うべきだと思います)

また、追加・削除・編集は禁止しなくても、DataTable にはこれらのイベントがあるので、変更の通知を受け取ることができます。なので、知らないうちに書き換えられたということを避けられます。まあ、かなり頑張らないといけないですけど…。

コメントを追加

Filtered HTML

  • ウェブページアドレスとメールアドレスは、自動的にハイパーリンクに変換されます。
  • 使用できるHTMLタグ: <a> <em> <strong> <cite> <code> <ul> <ol> <li> <dl> <dt> <dd> <blockquote> <img>
  • 次のタグを使用してソースコード構文をハイライトすることができます。: <code>, <blockcode>, <c>, <cpp>, <drupal5>, <drupal6>, <java>, <javascript>, <php>, <python>, <ruby> The supported tag styles are: <foo>, [foo].
  • 行と段落は自動的に折り返されます。

Plain text

  • HTMLタグは利用できません。
  • ウェブページアドレスとメールアドレスは、自動的にハイパーリンクに変換されます。
  • 行と段落は自動的に折り返されます。
CAPTCHA
This question is for testing whether you are a human visitor and to prevent automated spam submissions.
イメージ CAPTCHA
Enter the characters shown in the image.