プロパティ

# 自動実装プロパティ

自動実装プロパティは C# 3 で導入されました。
自動実装されたプロパティは、空のゲッターとセッター (アクセサー) で宣言されます:

public bool IsValid { get; set; }

自動実装プロパティがコードに記述されると、コンパイラは、プロパティのアクセサーを介してのみアクセスできる非公開の匿名フィールドを作成します。

上記の自動実装されたプロパティ ステートメントは、次の長いコードを記述することと同じです:

private bool _isValid;
public bool IsValid
{
    get { return _isValid; }
    set { _isValid = value; }
}

自動実装されたプロパティは、アクセサーにロジックを含めることはできません。例:

public bool IsValid { get; set { PropertyChanged("IsValid"); } } // Invalid code

自動実装プロパティはできます ただし、そのアクセサーには異なるアクセス修飾子があります:

public bool IsValid { get; private set; }    

C# 6 では、自動実装されたプロパティにセッターをまったく持たないようにすることができます (その値はコンストラクター内でのみ設定するか、ハードコードすることができるため、不変になります):

public bool IsValid { get; }    
public bool IsValid { get; } = true;

自動実装プロパティの初期化の詳細については、自動プロパティ初期化子のドキュメントを参照してください。

# パブリック Get

ゲッターは、クラスから値を公開するために使用されます。

string name;
public string Name
{
    get { return this.name; }
}

# 公開セット

セッターは、プロパティに値を割り当てるために使用されます。

string name;
public string Name 
{
    set { this.name = value; }
}

# プロパティへのアクセス

class Program 
{
    public static void Main(string[] args)
    {
        Person aPerson = new Person("Ann Xena Sample", new DateTime(1984, 10, 22));
        //example of accessing properties (Id, Name & DOB)
        Console.WriteLine("Id is:  \t{0}\nName is:\t'{1}'.\nDOB is: \t{2:yyyy-MM-dd}.\nAge is: \t{3}", aPerson.Id, aPerson.Name, aPerson.DOB, aPerson.GetAgeInYears());
        //example of setting properties

        aPerson.Name = "   Hans Trimmer  ";
        aPerson.DOB = new DateTime(1961, 11, 11);
        //aPerson.Id = 5; //this won't compile as Id's SET method is private; so only accessible within the Person class.
        //aPerson.DOB = DateTime.UtcNow.AddYears(1); //this would throw a runtime error as there's validation to ensure the DOB is in past. 

        //see how our changes above take effect; note that the Name has been trimmed
        Console.WriteLine("Id is:  \t{0}\nName is:\t'{1}'.\nDOB is: \t{2:yyyy-MM-dd}.\nAge is: \t{3}", aPerson.Id, aPerson.Name, aPerson.DOB, aPerson.GetAgeInYears());

        Console.WriteLine("Press any key to continue");
        Console.Read();
    }
}

public class Person
{
    private static int nextId = 0;
    private string name;
    private DateTime dob; //dates are held in UTC; i.e. we disregard timezones
    public Person(string name, DateTime dob)
    {
        this.Id = ++Person.nextId;
        this.Name = name;
        this.DOB = dob;
    }
    public int Id
    {
        get;
        private set;
    }
    public string Name
    {
        get { return this.name; }
        set
        {
            if (string.IsNullOrWhiteSpace(value)) throw new InvalidNameException(value);
            this.name = value.Trim();
        }
    }
    public DateTime DOB
    {
        get { return this.dob; }
        set 
        {
            if (value < DateTime.UtcNow.AddYears(-200) || value > DateTime.UtcNow) throw new InvalidDobException(value);
            this.dob = value; 
        }
    }
    public int GetAgeInYears()
    {
        DateTime today = DateTime.UtcNow;
        int offset = HasHadBirthdayThisYear() ? 0 : -1;
        return today.Year - this.dob.Year + offset;
    }
    private bool HasHadBirthdayThisYear()
    {
        bool hasHadBirthdayThisYear = true;
        DateTime today = DateTime.UtcNow;
        if (today.Month > this.dob.Month)
        {
            hasHadBirthdayThisYear = true;
        }
        else
        {
            if (today.Month == this.dob.Month)
            {
                hasHadBirthdayThisYear = today.Day > this.dob.Day;
            }
            else
            {
                hasHadBirthdayThisYear = false;
            }
        }
        return hasHadBirthdayThisYear;
    }
}

public class InvalidNameException : ApplicationException
{
    const string InvalidNameExceptionMessage = "'{0}' is an invalid name.";
    public InvalidNameException(string value): base(string.Format(InvalidNameExceptionMessage,value)){}
}
public class InvalidDobException : ApplicationException
{ 
    const string InvalidDobExceptionMessage = "'{0:yyyy-MM-dd}' is an invalid DOB.  The date must not be in the future, or over 200 years in the past.";
    public InvalidDobException(DateTime value): base(string.Format(InvalidDobExceptionMessage,value)){}
}

# プロパティのデフォルト値

デフォルト値の設定は、初期化子 (C#6) を使用して行うことができます

public class Name 
{
    public string First { get; set; } = "James";
    public string Last { get; set; } = "Smith";
}

読み取り専用の場合は、次のような値を返すことができます:


 public class Name 
  {
      public string First => "James";
      public string Last => "Smith";
  }

# コンテキスト内のさまざまなプロパティ

public class Person 
{
    //Id property can be read by other classes, but only set by the Person class
    public int Id {get; private set;}
    //Name property can be retrieved or assigned 
    public string Name {get; set;}
    
    private DateTime dob;
    //Date of Birth property is stored in a private variable, but retrieved or assigned through the public property.
    public DateTime DOB
    {
        get { return this.dob; }
        set { this.dob = value; }
    }
    //Age property can only be retrieved; it's value is derived from the date of birth 
    public int Age 
    {
        get 
        {
            int offset = HasHadBirthdayThisYear() ? 0 : -1;
            return DateTime.UtcNow.Year - this.dob.Year + offset;
        }
    }

    //this is not a property but a method; though it could be rewritten as a property if desired.
    private bool HasHadBirthdayThisYear() 
    {
        bool hasHadBirthdayThisYear = true;
        DateTime today = DateTime.UtcNow;
        if (today.Month > this.dob.Month)
        {
            hasHadBirthdayThisYear = true;
        }
        else 
        {
            if (today.Month == this.dob.Month)
            {
                hasHadBirthdayThisYear = today.Day > this.dob.Day;
            }
            else
            {
                hasHadBirthdayThisYear = false;
            }
        }
        return hasHadBirthdayThisYear;
    }
}

# 読み取り専用プロパティ

# 宣言

特に初心者がよく誤解しているのは、readonly でマークされた読み取り専用プロパティです。 キーワード。それは正しくありません。実際、以下はコンパイル時エラーです :

public readonly string SomeProp { get; set; }

getter しかない場合、プロパティは読み取り専用です。

public string SomeProp { get; }

# 読み取り専用プロパティを使用して不変クラスを作成する

public Address
{
    public string ZipCode { get; }
    public string City { get; }
    public string StreetAddress { get; }

    public Address(
        string zipCode,
        string city,
        string streetAddress)
    {
        if (zipCode == null)
            throw new ArgumentNullException(nameof(zipCode));
        if (city == null)
            throw new ArgumentNullException(nameof(city));
        if (streetAddress == null)
            throw new ArgumentNullException(nameof(streetAddress));

        ZipCode = zipCode;
        City = city;
        StreetAddress = streetAddress;
    }
}

# コメント

プロパティは、フィールドのクラス データ ストレージとメソッドのアクセシビリティを組み合わせます。プロパティを使用するか、フィールドを参照するプロパティを使用するか、フィールドを参照するメソッドを使用するかを決定するのが難しい場合があります。経験則として:

  • 値を取得または設定するだけのプロパティは、内部フィールドなしで使用する必要があります。他のロジックは発生しません。このような場合、内部フィールドを追加しても、コードを追加しても何のメリットもありません。
  • データを操作または検証する必要がある場合は、内部フィールドでプロパティを使用する必要があります。たとえば、文字列から先頭と末尾のスペースを削除したり、日付が過去のものではないことを確認したりできます。
  • メソッドとプロパティに関しては、両方を取得できます (get ) および更新 (set ) 値、プロパティの方が適しています。また、.Net は、クラスの構造を利用する多くの機能を提供します。例えばフォームにグリッドを追加すると、.Net はデフォルトでそのフォームのクラスのすべてのプロパティを一覧表示します。したがって、このような規則を最大限に活用するために、この動作が一般的に望ましい場合はプロパティを使用し、型が自動的に追加されないようにする場合はメソッドを使用するように計画してください。