「C Sharpの基礎 - プロパティ」の版間の差分
(ページの作成:「== 概要 == プロパティとは、 クラスの外部から見るとメンバ変数のように振る舞い、 クラス内部から見るとメソッドのように…」) |
細 (文字列「</source>」を「</syntaxhighlight>」に置換) |
||
(同じ利用者による、間の3版が非表示) | |||
7行目: | 7行目: | ||
アクセサ関数とは、以下の例のように、void Re(double x)やdouble Re()等のメンバ変数の値の取得・変更を行うためのメソッドのことである。<br> | アクセサ関数とは、以下の例のように、void Re(double x)やdouble Re()等のメンバ変数の値の取得・変更を行うためのメソッドのことである。<br> | ||
C++やJavaでは、アクセサ関数のメソッド名はvoid SetRe(double x)やdouble GetRe()というように、メンバ変数の数だけアクセサ関数が必要になってしまう。<br> | C++やJavaでは、アクセサ関数のメソッド名はvoid SetRe(double x)やdouble GetRe()というように、メンバ変数の数だけアクセサ関数が必要になってしまう。<br> | ||
< | <syntaxhighlight lang="c#"> | ||
using System; | using System; | ||
48行目: | 48行目: | ||
} | } | ||
} | } | ||
</ | </syntaxhighlight> | ||
<br><br> | <br><br> | ||
56行目: | 56行目: | ||
プロパティの定義の仕方は以下のようなシンタックスになる。<br> | プロパティの定義の仕方は以下のようなシンタックスになる。<br> | ||
set移行のブロックに値の変更用の処理を、get移行のに値の取得用の処理を記述する。<br> | set移行のブロックに値の変更用の処理を、get移行のに値の取得用の処理を記述する。<br> | ||
< | <syntaxhighlight lang="c#"> | ||
アクセスレベル 型名 プロパティ名 | アクセスレベル 型名 プロパティ名 | ||
{ | { | ||
72行目: | 72行目: | ||
} | } | ||
} | } | ||
</ | </syntaxhighlight> | ||
<br> | <br> | ||
例えば、上記の複素数クラスのアクセサをプロパティを使用して書き換えると以下のようになる。<br> | 例えば、上記の複素数クラスのアクセサをプロパティを使用して書き換えると以下のようになる。<br> | ||
< | <syntaxhighlight lang="c#"> | ||
using System; | using System; | ||
120行目: | 120行目: | ||
} | } | ||
} | } | ||
</ | </syntaxhighlight> | ||
<br><br> | <br><br> | ||
126行目: | 126行目: | ||
===== set / getで異なるアクセスレベルを設定 ===== | ===== set / getで異なるアクセスレベルを設定 ===== | ||
C# 2.0から、プロパティのset/getアクセサそれぞれ異なるアクセスレベルを設定できる。 | C# 2.0から、プロパティのset/getアクセサそれぞれ異なるアクセスレベルを設定できる。 | ||
< | <syntaxhighlight lang="c#"> | ||
class A | class A | ||
{ | { | ||
137行目: | 137行目: | ||
} | } | ||
} | } | ||
</ | </syntaxhighlight> | ||
<br> | <br> | ||
===== 自動プロパティ ===== | ===== 自動プロパティ ===== | ||
C# 3.0から、プロパティのget/setの中身の省略ができる。この機能を自動プロパティと呼ぶ。<br> | C# 3.0から、プロパティのget/setの中身の省略ができる。この機能を自動プロパティと呼ぶ。<br> | ||
また、コンパイラによって生成されるフィールド(上記の例では、__name)をバックフィールドと呼ぶ。<br> | また、コンパイラによって生成されるフィールド(上記の例では、__name)をバックフィールドと呼ぶ。<br> | ||
< | <syntaxhighlight lang="c#"> | ||
public string Name { get; set; } | public string Name { get; set; } | ||
</ | </syntaxhighlight> | ||
<br> | <br> | ||
上記の自動プロパティは、コンパイラは下記のように解釈する。<br> | 上記の自動プロパティは、コンパイラは下記のように解釈する。<br> | ||
< | <syntaxhighlight lang="c#"> | ||
private string __name; | private string __name; | ||
public string Name | public string Name | ||
154行目: | 154行目: | ||
set { this.__name = value; } | set { this.__name = value; } | ||
} | } | ||
</ | </syntaxhighlight> | ||
<br> | <br> | ||
自動プロパティを使用した複素数の例を記述する。<br> | 自動プロパティを使用した複素数の例を記述する。<br> | ||
< | <syntaxhighlight lang="c#"> | ||
using System; | using System; | ||
170行目: | 170行目: | ||
} | } | ||
} | } | ||
</ | </syntaxhighlight> | ||
<br> | <br> | ||
===== get-onlyプロパティ ===== | ===== get-onlyプロパティ ===== | ||
C# 6では、getアクセサだけのプロパティを定義できる。<br> | C# 6では、getアクセサだけのプロパティを定義できる。<br> | ||
この場合、コンストラクタ内のみ値を代入できる。(以降、書き換え不可になる)<br> | この場合、コンストラクタ内のみ値を代入できる。(以降、書き換え不可になる)<br> | ||
< | <syntaxhighlight lang="c#"> | ||
using System; | using System; | ||
190行目: | 190行目: | ||
} | } | ||
} | } | ||
</ | </syntaxhighlight> | ||
<br> | <br> | ||
上記のサンプルコードは、以下のような意味になる。(readonlyのバックフィールドが作成される)<br> | 上記のサンプルコードは、以下のような意味になる。(readonlyのバックフィールドが作成される)<br> | ||
< | <syntaxhighlight lang="c#"> | ||
using System; | using System; | ||
211行目: | 211行目: | ||
} | } | ||
} | } | ||
</ | </syntaxhighlight> | ||
<br> | <br> | ||
===== プロパティ初期化子 ===== | ===== プロパティ初期化子 ===== | ||
同じくC# 6.0から、自動プロパティに対して初期化子を与えることができる。<br> | 同じくC# 6.0から、自動プロパティに対して初期化子を与えることができる。<br> | ||
これにより、コンストラクタに記述しなくとも、プロパティに初期値を与えることができる。<br> | これにより、コンストラクタに記述しなくとも、プロパティに初期値を与えることができる。<br> | ||
< | <syntaxhighlight lang="c#"> | ||
class Point | class Point | ||
{ | { | ||
222行目: | 222行目: | ||
public int Y { get; set; } = 20; | public int Y { get; set; } = 20; | ||
} | } | ||
</ | </syntaxhighlight> | ||
<br> | <br> | ||
===== expression-bodiedなプロパティ ===== | ===== expression-bodiedなプロパティ ===== | ||
get-onlyのプロパティに限られるが、他のいくつかのメンバ関数と同様に、expression-bodied(本体が式の)形式でプロパティを定義できる。<br> | get-onlyのプロパティに限られるが、他のいくつかのメンバ関数と同様に、expression-bodied(本体が式の)形式でプロパティを定義できる。<br> | ||
上記の複素数クラスでは、Absプロパティの定義が簡潔に記述できる。<br> | 上記の複素数クラスでは、Absプロパティの定義が簡潔に記述できる。<br> | ||
< | <syntaxhighlight lang="c#"> | ||
using static System.Math; | using static System.Math; | ||
237行目: | 237行目: | ||
public double Abs => Sqrt(Re * Re + Im * Im); | public double Abs => Sqrt(Re * Re + Im * Im); | ||
} | } | ||
</ | </syntaxhighlight> | ||
<br><br> | <br><br> | ||
== 注意点 == | == 注意点 == | ||
C# | C#には、インデックス付きプロパティは存在しない。<br> | ||
C#では、コレクションクラスを返すプロパティが推奨される。<br> | |||
< | <br> | ||
また、配列やICollection型を返す場合は、IEnumerable型を返すべきである。<br> | |||
<syntaxhighlight lang="c#"> | |||
// 配列を返す場合 | // 配列を返す場合 | ||
// コンパイル可能 | |||
int[] x; | |||
public int[] X | |||
{ | |||
get => x; | |||
} | |||
// コンパイルエラー | // コンパイルエラー | ||
//int[] x; | //int[] x; | ||
//public int X[int i] | //public int X[int i] | ||
//{ | //{ | ||
// get | // get => x[i]; | ||
// private set | // private set => x[i] = value; | ||
//} | //} | ||
// | private List<int> listItems; // Itemsプロパティのフィールド | ||
[System.Runtime.CompilerServices.IndexerName("Items")] | |||
public int[] | public int this[int index] // インデクサ | ||
{ | { | ||
get | get => listItems[index]; | ||
set => listItems[index] = value; | |||
} | } | ||
// IEnumerableを返す場合 | // IEnumerableを返す場合 | ||
int[] x; | int[] x; | ||
public IEnumerable<int> X | public IEnumerable<int> X | ||
{ | { | ||
get { foreach (var item in x) yield return item; } | get => { | ||
foreach (var item in x) | |||
{ | |||
yield return item; | |||
} | |||
} | |||
} | } | ||
</ | </syntaxhighlight> | ||
<br> | <br> | ||
また、. | また、.NET Frameworkは、インデックス付きプロパティを認めている。(VB.NETはインデックス付きプロパティが存在する)<br> | ||
C#から呼び出す場合は、get_***というような名前のメソッドを呼び出す。<br> | C#から呼び出す場合は、get_***というような名前のメソッドを呼び出す。<br> | ||
例えば、VB.NETにてint X[int i]と言う名前でインデックス付きプロパティを定義した場合、 C#からはget_X(0)というように呼び出す。<br> | 例えば、VB.NETにてint X[int i]と言う名前でインデックス付きプロパティを定義した場合、 C#からはget_X(0)というように呼び出す。<br> | ||
<br> | <br> | ||
更に特殊な事情として、COMの場合のみ、X[0] | 更に特殊な事情として、COMの場合のみ、X[0]というような呼び出し方法が認められる。<br> | ||
詳しくは、COM相互運用時の特別処理を参照すること。<br> | |||
<br><br> | <br><br> | ||
__FORCETOC__ | __FORCETOC__ | ||
[[カテゴリ:C_Sharp]] | [[カテゴリ:C_Sharp]] |
2021年11月18日 (木) 15:10時点における最新版
概要
プロパティとは、 クラスの外部から見るとメンバ変数のように振る舞い、 クラス内部から見るとメソッドのように振舞うものである。
実装の隠蔽(カプセル化)の原則を崩すことなく、 アクセサ関数の煩雑さを解消するためのものである。
アクセサ関数とは
アクセサ関数とは、以下の例のように、void Re(double x)やdouble Re()等のメンバ変数の値の取得・変更を行うためのメソッドのことである。
C++やJavaでは、アクセサ関数のメソッド名はvoid SetRe(double x)やdouble GetRe()というように、メンバ変数の数だけアクセサ関数が必要になってしまう。
using System;
// 複素数クラス
class Complex
{
// 実装は外部から隠蔽する
private double re; // 実部を記憶しておく
private double im; // 虚部を記憶しておく
public double Re(){return this.re;} // 実部を取り出す
public void Re(double x){this.re = x;} // 実部を書き換え
public double Im(){return this.im;} // 虚部を取り出す
public void Im(double y){this.im = y;} // 虚部を書き換え
public double Abs(){return Math.Sqrt(re * re + im * im);} // 絶対値を取り出す
}
class Sample
{
static void Main()
{
// x = 5 + 1i
Complex x = new Complex();
x.Re(5); // x.re = 5
x.Im(1); // x.im = 1
// y = -2 + 3i
Complex y = new Complex();
y.Re(-2); // y.re = -2
y.Im( 3); // y.im = 3
Complex z = new Complex();
z.Re(x.Re() + y.Re()); // z.re = x.re + y.re
z.Im(x.Im() + y.Im()); // z.im = x.im + y.im
Console.Write("|{0} + {1}i| = {2}\n", z.Re(), z.Im(), z.Abs());
// |3 + 4i| = 5 と表示される
}
}
プロパティ
アクセサ関数の煩雑さを減らすために、クラス内部から見るとメソッドのように振る舞い、 クラス利用側から見るとメンバ変数のように振舞うプロパティ機能がある。
プロパティの定義の仕方は以下のようなシンタックスになる。
set移行のブロックに値の変更用の処理を、get移行のに値の取得用の処理を記述する。
アクセスレベル 型名 プロパティ名
{
set
{
// setアクセサ(setterとも言う)
// ここに値の変更時の処理を記述する
// valueという名前の変数に代入された値が格納される
}
get
{
// getアクセサ(getterとも言う)
// ここに値の取得時の処理を記述する
// メソッドの場合と同様に、値はreturnキーワードを用いて返す
}
}
例えば、上記の複素数クラスのアクセサをプロパティを使用して書き換えると以下のようになる。
using System;
// 複素数クラス
class Complex
{
// 実装は外部から隠蔽(privateにしておく)
private double re; // 実部を記憶しておく
private double im; // 虚部を記憶しておく
// 実部の取得・変更用のプロパティ
public double Re
{
set { this.re = value; }
get { return this.re; }
}
// 虚部の取得・変更用のプロパティ
public double Im
{
set { this.im = value; }
get { return this.im; }
}
// 絶対値の取得用のプロパティ
public double Abs
{
// 読み取り専用プロパティ。
// setブロックを書かない。
get { return Math.Sqrt(re * re + im * im); }
}
}
class Sample
{
static void Main()
{
Complex c = new Complex();
c.Re = 4; // Reプロパティのsetアクセサが呼び出される
c.Im = 3; // Imプロパティのsetアクセサが呼び出される
Console.Write("|{0} + ", c.Re); // Reプロパティのgetアクセサが呼び出される
Console.Write("{0}i| =", c.Im); // Imプロパティのgetアクセサが呼び出される
Console.Write(" {0}\n", c.Abs); // Absプロパティのgetアクセサが呼び出される
}
}
set / getで異なるアクセスレベルを設定
set / getで異なるアクセスレベルを設定
C# 2.0から、プロパティのset/getアクセサそれぞれ異なるアクセスレベルを設定できる。
class A
{
private int n;
public int N
{
get{ return this.n; }
protected set{ this.n = value; }
}
}
自動プロパティ
C# 3.0から、プロパティのget/setの中身の省略ができる。この機能を自動プロパティと呼ぶ。
また、コンパイラによって生成されるフィールド(上記の例では、__name)をバックフィールドと呼ぶ。
public string Name { get; set; }
上記の自動プロパティは、コンパイラは下記のように解釈する。
private string __name;
public string Name
{
get { return this.__name; }
set { this.__name = value; }
}
自動プロパティを使用した複素数の例を記述する。
using System;
class Complex
{
public double Re { get; set; }
public double Im { get; set; }
public double Abs
{
get { return Math.Sqrt(Re * Re + Im * Im); }
}
}
get-onlyプロパティ
C# 6では、getアクセサだけのプロパティを定義できる。
この場合、コンストラクタ内のみ値を代入できる。(以降、書き換え不可になる)
using System;
class Complex
{
public double Re { get; }
public double Im { get; }
public Complex(double re, double im)
{
// コンストラクタ内のみ代入可能
Re = re;
Im = im;
}
}
上記のサンプルコードは、以下のような意味になる。(readonlyのバックフィールドが作成される)
using System;
class Complex
{
private readonly double _re;
private readonly double _im;
public double Re { get { return _re; } }
public double Im { get { return _im; } }
public Complex(double re, double im)
{
// コンストラクタ内のみ代入可能
_re = re;
_im = im;
}
}
プロパティ初期化子
同じくC# 6.0から、自動プロパティに対して初期化子を与えることができる。
これにより、コンストラクタに記述しなくとも、プロパティに初期値を与えることができる。
class Point
{
public int X { get; set; } = 10;
public int Y { get; set; } = 20;
}
expression-bodiedなプロパティ
get-onlyのプロパティに限られるが、他のいくつかのメンバ関数と同様に、expression-bodied(本体が式の)形式でプロパティを定義できる。
上記の複素数クラスでは、Absプロパティの定義が簡潔に記述できる。
using static System.Math;
class Complex
{
public double Re { get; set; }
public double Im { get; set; }
public double Abs => Sqrt(Re * Re + Im * Im);
}
注意点
C#には、インデックス付きプロパティは存在しない。
C#では、コレクションクラスを返すプロパティが推奨される。
また、配列やICollection型を返す場合は、IEnumerable型を返すべきである。
// 配列を返す場合
// コンパイル可能
int[] x;
public int[] X
{
get => x;
}
// コンパイルエラー
//int[] x;
//public int X[int i]
//{
// get => x[i];
// private set => x[i] = value;
//}
private List<int> listItems; // Itemsプロパティのフィールド
[System.Runtime.CompilerServices.IndexerName("Items")]
public int this[int index] // インデクサ
{
get => listItems[index];
set => listItems[index] = value;
}
// IEnumerableを返す場合
int[] x;
public IEnumerable<int> X
{
get => {
foreach (var item in x)
{
yield return item;
}
}
}
また、.NET Frameworkは、インデックス付きプロパティを認めている。(VB.NETはインデックス付きプロパティが存在する)
C#から呼び出す場合は、get_***というような名前のメソッドを呼び出す。
例えば、VB.NETにてint X[int i]と言う名前でインデックス付きプロパティを定義した場合、 C#からはget_X(0)というように呼び出す。
更に特殊な事情として、COMの場合のみ、X[0]というような呼び出し方法が認められる。
詳しくは、COM相互運用時の特別処理を参照すること。