サーチ…
備考
プロパティは、フィールドのクラスデータストレージとメソッドのアクセシビリティを結合します。プロパティ、フィールドを参照するプロパティ、またはフィールドを参照するメソッドのどちらを使用するかを決めるのが難しい場合があります。経験則として、
値が取得および/または設定される場合のみ、プロパティは内部フィールドなしで使用する必要があります。他のロジックは発生しません。そのような場合、内部フィールドを追加すると、利益のためにコードを追加することになります。
データを操作または検証する必要があるときは、プロパティを内部フィールドとともに使用する必要があります。たとえば、文字列の先頭と末尾のスペースを削除したり、日付が過去でないことを確認したりすることがあります。
メソッドvsプロパティに関して、値の取得( get
)と更新( set
)の両方が可能な場合は、プロパティが適しています。また、.Netはクラスの構造を利用する多くの機能を提供します。たとえば、フォームにグリッドを追加すると、.Netはデフォルトでそのフォームのクラスのすべてのプロパティを一覧表示します。したがって、このような規則は、この振る舞いが一般的に望ましい場合にプロパティを使用することを計画しています。また、自動的に型を追加したくないメソッドもあります。
コンテキスト内のさまざまなプロパティ
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;
}
}
公共ゲット
ゲッターは、クラスから値を公開するために使用されます。
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)){}
}
プロパティのデフォルト値
デフォルト値を設定するには、Initializers(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";
}
自動実装されたプロパティ
自動実装されたプロパティはC#3で導入されました。
自動実装されたプロパティは、空のgetterおよびsetter(アクセサ)で宣言されます。
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;
自動実装されたプロパティの初期化の詳細については、 Auto-property initializersのドキュメントを参照してください。
読み取り専用プロパティ
宣言
一般的な誤解、特に初心者は、 readonly
プロパティは、 readonly
キーワードでマークされているプロパティです。それは正しくないと実際に次のコンパイル時のエラーです:
public readonly string SomeProp { get; set; }
プロパティはゲッターを持っている場合のみ読み取り専用です。
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;
}
}