プロパティ
プログラミング言語においてプロパティという用語は多義的に使われますが、C#言語においてはアクセッサーの機能を持つ文法です。
Java言語やC++言語といった他のメジャーなプログラミング言語にはない機能です。
クラスが持つ状態を表すフィールドをカプセル化するための記述が簡単になります。
使う方からはフィールド(変数)のように扱え、実装する側からはメソッド(関数)のように扱えます。
インライン展開されるのでメソッドを呼ぶようなオーバーヘッドがない利点もあります。
(プロパティ内の処理が複雑な場合など、必ずしもインライン展開される訳でもない)
実装例
これをプロパティを使わず
にカプセル化するには、フィールドを非公開にし、取得用と設定用のメソッドを用意します。
private string _name;
public string GetName() => _name;
public void SetName( string name) => _name = name;
|
これをプロパティを使って定義すると以下のようになります
なお、取得用のgetと設定用のsetとsetの中の変数名であるvalueは予約語になります
private string _name;
public string Name
{
get => _name;
set => _name = value;
}
|
Visual Studioによる簡単な記述法
Visual Studio上でフィールド定義の変数名を選択してコンテキストメニューを開き、「クイックアクションとリファクタリング」→「フィールドのカプセル化」を選択すると展開してもらえます。
自動プロパティ
上記のような定義を大量に記述するのは大変ですが、簡易に記述することができます。
コンパイル時に上の実装例にように展開されます。
public string Name { get ; set ; }
|
異なるアクセス指定の方法
setterとgetterとで異なるアクセスレベルを指定可能です
public string Name { get ; private set ; }
|
自動プロパティの初期化子
public string Name { get ; set ; } = string .Empty;
|
Getterのみの自動プロパティ
public string Company { get ;} = "hogehoge株式会社" ;
public string Address => "東京都千代田区" ;
|
Getterによる状態変更
下記の例では年齢を参照する毎に老いていくことになる。副作用あるコードは要注意。
public class Person
{
private int _age;
public int Age => _age++;
}
|
カプセル化の利点
他クラスからのアクセスを制御でき、入力値チェックや値変更のイベント惹起など処理を介在させることが可能となります。
下記の例では年齢にマイナス値が入ることはありません。
<pre class = "wp-block-syntaxhighlighter-code" >
public class Person
{
private int _age;
public int Age
{
get => _age;
set
{
if (value < 0)
throw new ArgumentOutOfRangeException();
_age = value;
}
}
}</pre>
|
プロパティの値変更時に処理を実行する
public class Person
{
private int _age;
public int Age
{
get => _age;
set
{
if (_age != value)
{
_age = value;
DoSomethingWhenAgeChanged(_age);
}
}
}
private void DoSomethingWhenAgeChanged( int age)
{
Console.WriteLine($ "age changed to {age}" );
}
}
|
上記コードでも一応は問題ないのですが、プロパティの目的であるアクセッサの責務からは外れているとの議論はあります。
このような場合はプロパティではなく通常のメソッドを用意した方がよいかもしれません。
public class Person
{
private int _age;
public int Age => _age;
public void SetAge( int age)
{
if (_age != age)
{
_age = age;
DoSomethingWhenAgeChanged(_age);
}
}
private void DoSomethingWhenAgeChanged( int age)
{
Console.WriteLine($ "age changed to {age}" );
}
}
|
または、プロパティの値変更イベントを介して処理を実行する方法もあります。
public class Person: System.ComponentModel.INotifyPropertyChanged
{
public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged;
private int _age;
public int Age
{
get => _age;
set
{
if (_age != value)
{
_age = value;
PropertyChanged?.Invoke( this , new System.ComponentModel.PropertyChangedEventArgs(nameof(Age)));
}
}
}
public Person()
{
PropertyChanged += (_, e) =>
{
if (e.PropertyName == nameof(Age))
DoSomethingWhenAgeChanged(Age);
};
}
private void DoSomethingWhenAgeChanged( int age)
{
Console.WriteLine($ "age changed to {age}" );
}
}
|