クラスメンバーのひとつのプロパティ(property)についてです。
プロパティはクラス内部のデータを外部とやり取りするためにC#が用意した機能です。
プロパティを使い、クラスのプライベートフィールドの値の取得や変更するために使ったり、
受け取った値を加工してプライベートフィールドに設定したり、
プライベートフィールドの値を加工して返したりすることができます。
なんのためにこんなプロパティがあるのかというと、クラスの外部(呼び出す側)に対して必要最低限の情報を公開することで、修正時にクラス内部の変更だけで済むようしたり、外部からの誤操作でデータが書き換えられてしまうことを防ぐことを目的にしています。
クラスってなに?という方はこちらの記事をどうぞ。
プロパティの定義
プロパティの定義はこんな感じです。
プロパティにはデータを取得するためのgetアクセサー(accessor)とデータを設定するためのsetアクセサーがあります。
アクセスレベル 戻り値の型 プロパティ名 {
アクセスレベル get
{
// getアクセサー
// 外部からプロパティを取得しようとしたときに呼ばれる
// { } ブロック内でreturnを使い値を返す
}
アクセスレベル set
{
// setアクセサー
// 外部からプロパティに値が渡されたときに呼ばれる
// 外部から渡された値は変数valueに設定されている
}
}
アクセスレベルにはprivate、publicなどを指定します。
プロパティ本体、getアクセサー、setアクセサーそれぞれにアクセスレベルを指定することが出来ます。getは外部に公開して、setは内部だけという使い方が出来ます。
プロパティ本体のアクセスレベルを省略した場合はprivate、
get、setのアクセスレベルを省略した場合はpublicになるみたいです。
戻り値の型はデータ型を指定します。これがgetアクセサーの戻り値の型、setアクセサーに渡される変数valueの型になります。
getアクセサーは外部からプロパティを取得しようとしたときに呼ばれるメソッドです。returnを使い値を返します。{ }ブロック内からクラスメンバーにアクセスことが出来ます。
setアクセサーは外部からプロパティに値が設定されたときに呼ばれるメソッドです。
外部から与えられた値がvalueという変数に格納されています。
同じ名前のフィールドがクラス内にある場合は、thisを使いクラスメンバー(this.value)と変数valueを区別します。こちらも{ } ブロック内からクラスメンバーにアクセスすることが出来ます。
get、set両方のアクセサーを持つプロパティは読み取り/書き込み
getアクセサーのみを持つプロパティは読み取り専用、
setアクセサーのみを持つプロパティは書き込み専用になります。
サンプルコードです。
using System;
class Program
{
public static void Main()
{
var cls = new Sample();
// プロパティに値を設定(setアクセサーが呼ばれる)
cls.prop1 = 10;
// プロパティの値を取得(getアクセサーが呼ばれる)
Console.WriteLine(cls.prop1);
}
}
class Sample
{
// プライベートフィールド
private int privateData;
// プロパティ
public int prop1 {
// getアクセサー(プライベートフィールドを返す)
get { return this.privateData; }
// setアクセサー(プライベートフィールドに値を設定)
set { this.privateData = value; }
}
}
クラス内でプロパティの値を保存している変数(サンプルだと変数privateData)をバッキングフィールド(backing field)と言います。
get、setアクセサーのブロック内が1つの式だけの場合、=> を使い { } を省略することが出来ます。
class Sample
{
// プライベートフィールド
private int privateData;
// プロパティ
public int prop1
{
// getアクセサー(プライベートフィールドを返す)
get => this.privateData;
// setアクセサー(プライベートフィールドに値を設定)
set => this.privateData = value;
}
}
get、set内で値を加工する
getアクセサーでプライベートフィールドの値を加工して返したり、
setアクセサーで受け取った値を加工してプライベートフィールドに設定することもできます。
class SampleClass {
// プライベートフィールド
private int privateData;
// プロパティ
public int prop1 {
get {
//プライベートフィールドの値を加工して返す
return this.privateData + 100;
}
set {
//受け取った値を加工してプライベートフィールドに設定
this.privateData = value + 200;
}
}
}
自動実装プロパティ
get、setアクセサーの処理の記述は省略することができます。
省略した場合は、プロパティとその値を保存するバッキングフィールドが自動で作成されます。これを自動実装プロパティといいます。
プロパティ名とプロパティの値を保存するバッキングフィールド名が同じ名前になります。下のサンプルで言うと、外部からのプロパティへのアクセスも内部からのフィールドへのアクセスもprop1という名前を使うということです。
getアクセサーだけのプロパティも自動実装プロパティが使えます。
setアクセサーだけのプロパティはダメ。
using System;
class Program
{
public static void Main()
{
var cls = new Sample();
// プロパティに値を設定(setアクセサーが呼ばれる)
cls.prop1 = 10;
// プロパティの値を取得(getアクセサーが呼ばれる)
Console.WriteLine(cls.prop1);
}
}
class Sample
{
// 読み込み/書き込みが出来るプロパティ
public int prop1 { get; set; }
// 読み込み専用プロパティ(値の設定はクラス内部で行う)
public int prop2 { get; }
public void method()
{
// バッキングフィールドにアクセス
Console.WriteLine(this.prop1);
}
}
オブジェクト初期化子
クラスのオブジェクトを作成する際に、同時にアクセスできる(publicが付いてるってこと)フィールドやプロパティに値を設定することができます。この書き方をオブジェクト初期化子といいます。
using System;
class SampleClass {
// プライベートフィールド
private int privateData;
// プロパティ
public int prop1 {
// getアクセサー(プライベートフィールドを返す)
get => this.privateData;
// setアクセサー(プライベートフィールドに値を設定)
set => this.privateData = value;
}
public string prop2 { get; set; }
}
class Program {
static void Main() {
//SampleClassのオブジェクト作成と同時にプロパティprop1とprop2に値を設定
SampleClass cls = new SampleClass() { prop1 = 100, prop2 = "abc" };
Console.WriteLine($"prop1={cls.prop1}, prop2={cls.prop2}");
}
}
実行結果
prop1=100, prop2=abc
21行目でSampleClassのオブジェクト作成と同時にプロパティprop1とprop2に値を設定しています。後ろの波かっこの部分で値を設定します。
new XXXXX() { プロパティ名 = 値 };
init-onlyプロパティ
setアクセサーの代わりにinitと書くと、そのプロパティの値はコンストラクタかオブジェクト初期化子でしか設定できなくなります。
これを使うとオブジェクト作成以降は値が変更できないプロパティを定義できます。
class SampleClass {
// プライベートフィールド
private int privateData;
// プロパティ
public int prop1 { get; init; }
// コンストラクタ
public SampleClass() { }
public SampleClass(int value) { this.prop1 = value; }
// メソッド
public void Method() {
//initを使うとクラス内からも変更不可になる
//this.prop1 = 999; //エラー
}
}
class Program {
static void Main() {
//コンストラクタでプロパティprop1の値を設定
SampleClass cls1 = new SampleClass(500);
//SampleClassのオブジェクト作成と同時にプロパティprop1に値を設定(オブジェクト初期化子)
SampleClass cls2 = new SampleClass() { prop1 = 100 };
//これはエラーになる-------
//cls1.prop1 = 100;
//cls2.prop1 = 200;
//----------------------
}
}
コンストラクタ?メソッド?という方はこちらの記事をどうぞ
C# 記事まとめページに戻る(他のサンプルコードもこちら)
コメント