この記事の英語版を表示するには、[英語] のチェック ボックスをオンにしてください。また、テキストにマウス ポインターを合わせると、ポップアップ ウィンドウに英語のテキストを表示することもできます。
翻訳
英語
このドキュメントはアーカイブされており、メンテナンスされていません。

プロパティとメソッドの選択

一般的に、メソッドは操作を表し、プロパティはデータを表します。 プロパティはフィールドのように使用されるため、計算上複雑にしたり、副作用を生成したりしないようにする必要があります。 経験の浅い開発者の場合、メソッドよりもプロパティを使用する方が容易です。そのため、以下のガイドラインに違反しない場合は、メソッドではなくプロパティを使用することを検討してください。

メンバーが型の論理属性を表す場合は、プロパティを使用することを検討してください。

たとえば、BorderStyle は、境界線のスタイルが ListView の属性であるため、プロパティです。

プロパティの値がプロセス メモリに格納され、プロパティが値へのアクセスだけを提供する場合は、メソッドではなく、プロパティを使用してください。

このガイドラインを次のコード例に示します。 EmployeeRecord クラスは、プライベート フィールドへのアクセスを提供する 2 つのプロパティを定義します。 完全なコード例は、このトピックの最後に示します。


public class EmployeeRecord
{
    private int employeeId;
    private int department;
    public EmployeeRecord()
    {
    }
    public  EmployeeRecord (int id, int departmentId)
    {
        EmployeeId = id;
        Department = departmentId;
    }
    public int Department
    {
        get {return department;}
        set {department = value;}
    }
    public int EmployeeId
    {
        get {return employeeId;}
        set {employeeId = value;}
    }
    public EmployeeRecord Clone()
    {
        return new EmployeeRecord(employeeId, department);
    }
}


以下の場合は、プロパティではなくメソッドを使用してください。

  • 操作がフィールド セットの場合よりも格段に低速の場合。 スレッドのブロックを避けるために非同期バージョンの操作を提供することも検討している場合は、プロパティでは操作の負荷が大きくなりすぎる可能性があります。 特に、初期化時以外にもネットワークやファイル システムにアクセスする操作は、多くの場合、プロパティではなく、メソッドにする必要があります。

  • 操作が Object.ToString method などの変換操作の場合。

  • パラメーターを変更しない場合でも、呼び出されるたびに異なる結果を返す操作の場合。 たとえば、NewGuid メソッドは、呼び出されるたびに異なる値を返します。

  • 操作によって重大かつ顕著な副作用が生じる場合。 内部キャッシュへの読み込みは、通常、顕著な副作用とは見なされません。

  • 操作によって内部状態のコピーが返される場合 (これには、スタックに返される値型オブジェクトのコピーは含まれません)。

  • 操作によって配列が返される場合。

内部配列を保持するには、プロパティによって使用されている配列への参照ではなく、配列の詳細コピーを返す必要があるため、操作によって配列が返される場合はメソッドを使用します。 このことは、開発者がプロパティをフィールドのように使用するという事実とも重なって、きわめて非効率なコードが生じる可能性があります。 これを次のコード例に示します。この例では、プロパティを使って配列を返します。 完全なコード例は、このトピックの最後に示します。


public class EmployeeData
{
    EmployeeRecord[] data;
    public EmployeeData(EmployeeRecord[] data)
    {
        this.data = data;
    }
    public EmployeeRecord[] Employees
    {
        get 
        {
            EmployeeRecord[] newData = CopyEmployeeRecords();
            return newData;
        }
    }
    EmployeeRecord[] CopyEmployeeRecords()
    {
        EmployeeRecord[] newData = new EmployeeRecord[data.Length];
        for(int i = 0; i< data.Length; i++)
        {
            newData[i] = data[i].Clone();
        }
        Console.WriteLine ("EmployeeData: cloned employee data.");
        return newData;
    }
}


このクラスを使用する開発者は、プロパティがフィールド アクセスと同様に負荷が大きくないと想定し、この想定に基づいて、次のコード例に示すようなアプリケーション コードを記述します。


public class RecordChecker
{
    public static Collection<int> FindEmployees(EmployeeData dataSource, 
             int department)
    {
        Collection<int> storage = new Collection<int>();
        Console.WriteLine("Record checker: beginning search.");
        for (int i = 0; i < dataSource.Employees.Length; i++)
        {
            if (dataSource.Employees[i].Department == department)
            {
                Console.WriteLine("Record checker: found match at {0}.", i);
                storage.Add(dataSource.Employees[i].EmployeeId);
                Console.WriteLine("Record checker: stored match at {0}.", i);
            }
            else 
            {
                Console.WriteLine("Record checker: no match at {0}.", i);
            }
        }
        return storage;
    }
}


Employees プロパティはループを反復処理するたびにアクセスされ、また部門が一致するときにもアクセスされます。 プロパティにアクセスするたびに従業員配列のコピーが作成され、一時的に使用されてから、ガベージ コレクションを要求します。 Employees をメソッドとして実装することにより、このアクションがフィールドへのアクセスよりも計算負荷が大きいことを開発者に示します。 これにより、開発者は、処理を実行する際にメソッドを 1 回だけ呼び出し、メソッド呼び出しの結果をキャッシュするという方法を選択するようになります。

次のコード例は、プロパティ アクセスの計算負荷が小さいことを前提にした完全なアプリケーションを示しています。 EmployeeData クラスは、配列のコピーを返すプロパティを正しく定義していません。


using System;
using System.Collections.ObjectModel;
namespace Examples.DesignGuidelines.Properties
{
    public class EmployeeRecord
    {
        private int employeeId;
        private int department;
        public EmployeeRecord()
        {
        }
        public  EmployeeRecord (int id, int departmentId)
        {
            EmployeeId = id;
            Department = departmentId;
        }
        public int Department
        {
            get {return department;}
            set {department = value;}
        }
        public int EmployeeId
        {
            get {return employeeId;}
            set {employeeId = value;}
        }
        public EmployeeRecord Clone()
        {
            return new EmployeeRecord(employeeId, department);
        }
    }

public class EmployeeData
{
    EmployeeRecord[] data;
    public EmployeeData(EmployeeRecord[] data)
    {
        this.data = data;
    }
    public EmployeeRecord[] Employees
    {
        get 
        {
            EmployeeRecord[] newData = CopyEmployeeRecords();
            return newData;
        }
    }
    EmployeeRecord[] CopyEmployeeRecords()
    {
        EmployeeRecord[] newData = new EmployeeRecord[data.Length];
        for(int i = 0; i< data.Length; i++)
        {
            newData[i] = data[i].Clone();
        }
        Console.WriteLine ("EmployeeData: cloned employee data.");
        return newData;
    }
}

public class RecordChecker
{
    public static Collection<int> FindEmployees(EmployeeData dataSource, 
             int department)
    {
        Collection<int> storage = new Collection<int>();
        Console.WriteLine("Record checker: beginning search.");
        for (int i = 0; i < dataSource.Employees.Length; i++)
        {
            if (dataSource.Employees[i].Department == department)
            {
                Console.WriteLine("Record checker: found match at {0}.", i);
                storage.Add(dataSource.Employees[i].EmployeeId);
                Console.WriteLine("Record checker: stored match at {0}.", i);
            }
            else 
            {
                Console.WriteLine("Record checker: no match at {0}.", i);
            }
        }
        return storage;
    }
}
    public class Tester
    {
        public static void Main()
        {
            EmployeeRecord[] records  = new EmployeeRecord[3];
            EmployeeRecord r0  = new EmployeeRecord();
            r0.EmployeeId = 1;
            r0.Department = 100;
            records[0] = r0;
            EmployeeRecord r1  = new EmployeeRecord();
            r1.EmployeeId = 2;
            r1.Department = 100;
            records[1] = r1;
            EmployeeRecord r2  = new EmployeeRecord();
            r2.EmployeeId = 3;
            r2.Department = 101;
            records[2] = r2;
            EmployeeData empData = new EmployeeData(records);
            Collection<int> hits = RecordChecker.FindEmployees(empData, 100);
            foreach (int i in hits)
            {
                Console.WriteLine("found employee {0}", i);
            }
        }
    }
}



Portions Copyright 2005 Microsoft Corporation. All rights reserved.

Portions Copyright Addison-Wesley Corporation. All rights reserved.

設計ガイドラインの詳細についてを参照してください、「フレームワークの設計ガイドライン。規則、慣用句、および再利用可能なパターン。ネット ライブラリ」本クシシュトフ Cwalina、ブラッド エイブラムス、アスキー、2005 年発表しました。

表示: