C#

【C#】プロパティって何?フィールドとの違いは2つ

アイキャッチ(c_sharp)

こんにちは!
なかむぅです。

C#でコードを書いていると

これプロパティとフィールドどっちだっけ

とわからなくなり、調べているうちに

そもそもプロパティって何

といった、そもそもの疑問に至ってしまったので調べてみました。

プロパティとフィールドの違い

プロパティとフィールドの違いは以下2つ。

  • アクセサが付いているかついてないか
  • フィールドにアクセスするためのものかどうか

プロパティにはセッターとゲッターというアクセサがついている

まず、プロパティとフィールドの書き方です。


public int hoge; // フィールド
public int Hoge { get; set; } // プロパティ

見た通り、プロパティはget、setのアクセサがついたもので、フィールドはこのアクセサがついてないもの

しかし、このプロパティですが、実は内部的にコンパイラーがフィールドを生成しており、見えないフィールドに対して値の受け渡しをしています。

プロパティとバッキングフィールド

「プロパティは内部的にコンパイラーがフィールドを生成している」と説明しましたが、プロパティに値をセットするときは以下と同様の動きをしています。


private int _hoge; // バッキングフィールド
public int Hoge
{
	get
	{
		return _hoge;
	}
	set
	{
		_hoge = value;
	}
}

プロパティにはバッキングフィールド(BackingField)というフィールドが用意されており、このバッキングフィールドに対して値の操作を行う仕組みがプロパティです。

コードを見るとget時はバッキングフィールドをreturnで返し、set時はHogeプロパティに代入した値がvalueとして渡され、バッキングフィールドにセットされていますね。

要は「プロパティはフィールドにアクセスするためのもの」ということになります。

フィールドを書かなくても、実は見えないフィールドが作られていて、プロパティはそのフィールドに対してアクセスをしているんですね!

プロパティとフィールドの使い分け方

オブジェクト指向においてフィールドの値を外部から変更させたり、参照させないために、「カプセル化」というものが重要になってきます。

そのため、フィールドは必ずprivateで書き、このフィールドにアクセスするためのプロパティをpublicで書きましょう。

フィールドの値を参照したり、変更する場合は必ずプロパティを経由するようにしてください。

以下のようにフィールドの値を参照、またはセットする前に何か処理を入れることもできます。


private int _hoge; // プロパティHogeで操作するバッキングフィールド
public int Hoge
{
	get
	{
		return _hoge + 2;
	}
	set
	{
		_hoge = value * 2;
	}
}

XamarinのSetPropertyなど、データバインディングするために利用することもありますね。


private int _hoge;
public int Hoge
{
	get
	{
		return _hoge + 2;
	}
	set
	{
		SetProperty(ref _hoge, value);
	}
}

その他プロパティの書き方

gtter、setterのみで書く

とくにフィールドを操作することがないのであれば、シンプルに


public int Hoge { get; set; }

といった書き方でいいと思います。

セッターをprivateにすることで、外部からの値の変更を防ぐ書き方もできます。


public int Hoge { get; private set; }

ラムダ式で書く

フィールドを操作するときは、以下のようにラムダ式でスッキリ書くこともできるので覚えておきましょう。
(C#7.0以降)


private int _hoge;
public int Hoge
{
	get => _hoge + 2;
	set => _hoge = value * 2;
}

ちなみにgetterだけならこれでOK。


private int _hoge;
public int Hoge => _hoge + 2;

プロパティとフィールドの速度の違い

プロパティとフィールドの違いを調べていると「速度」というキーワードが気になったのでついでに試してみました。

下記の記事によるとプロパティのほうが速いと記載してあります。

下記コードで試してみました。


private int _hoge;
public int Hoge { get; set; }

public void Field()
{
	var sw = new Stopwatch();

	sw.Start();

	for (int i = 0; i < 1000000000; i++)
	{
		_hoge = 1000;
	}

	sw.Stop();

	Console.WriteLine($"Field	:{sw.ElapsedMilliseconds} ms");
}

public void Property()
{
	var sw = new Stopwatch();

	sw.Start();

	for (int i = 0; i < 1000000000; i++)
	{
		Hoge = 1000;
	}

	sw.Stop();

	Console.WriteLine($"Property :{sw.ElapsedMilliseconds} ms");
}

シンプルにフィールド、プロパティそれぞれに値を10億回代入する処理です。

それぞれ最後まで代入し終わる時間をストップウォッチで測ります。

結果は以下のようになりました。


Field    :1252 ms
Property :1937 ms
いや、フィールドの方が速いやん

一応参考記事のコードを丸コピして実行。


Total field      : 10000000000 : 47301 ms
Total properties : 10000000000 : 78750 ms
いや、やっぱフィールドの方が速いやん

「使っているのがMacだからかなぁ」とか「.Net Core3.1だからかなぁ」とか考えてWindowsで試したり.Net Core5.0でやってみたり、コメントアウトしてフィールドのみとプロパティのみで計測してみたりしてみましたが結果は変わらずでした。

なぜフィールドのほうが速いのか

結果、フィールドのほうが速かったわけですが、なぜフィールドが速いのでしょう。

プロパティとフィールドにはアクセサを通るかどうかの違いがあるので、直接的か間接的かの違いで速さが変わるのだと思います。

そこで、以下のような直接値をセットするか、メソッドを経由してセットするかのコードで試してみました。


private int _hoge;

public void FieldOnly()
{
	var sw = new Stopwatch();

	sw.Start();

	for (int i = 0; i < 1000000000; i++)
	{
		_hoge = 1000;
	}

	sw.Stop();

	Console.WriteLine($"FieldOnly	  :{sw.ElapsedMilliseconds} ms");
}

public void FieldAndMethod()
{
	var sw = new Stopwatch();

	sw.Start();

	for (int i = 0; i < 1000000000; i++)
	{
		SetField(1000);
	}

	sw.Stop();

	Console.WriteLine($"FieldAndMethod :{sw.ElapsedMilliseconds} ms");
}

private void SetField(int value)
{
	_hoge = value;
}

結果は...


FieldOnly      :1264 ms
FieldAndMethod :1944 ms

やっぱメソッドを経由するので遅いですよね。

まぁそうゆうことだと思います。


私の会社で一緒に働きませんか?

私が働く会社では、一緒に働いてくれるエンジニアを募集しています♪

こんな人がオススメ
  • 長く働いていても年収が全然上がらない...一気に年収を上げたい
  • 残業をしたくてしているわけではないので、残業代はしっかりと出して欲しい
  • やりたいことに挑戦させてもらえない...自分がしてみたいことに挑戦したい
  • 通勤時間が長すぎるから、できるだけ短くしたい
  • もっと勉強したいから、書籍代や資格の受験料など負担してくれるところに入りたい
  • 現場に駆り出されてからは放置プレイ...相談できる人も先輩も居ないので、自分の状況をちゃんと理解してくれるところで働きたい

上記内容に1つでも当てはまる場合は、ぜひお声がけください。
私のサイトから応募していただいた方にはお好きなギフト券5000円分プレゼントさせていただきます(条件あり)。

詳細ページ


まとめ

プロパティはフィールドのアクセサであり、カプセル化には必要不可欠なものだということがわかりました。

フィールドが必要な場合や、必要ない場合など様々なので、case-by-caseで使い分けていきまししょう。

以上、プロパティが何者かかということと、プロパティとフィールドの違いについてでした!

いやここは違うだろ

と思った方はコメントいただけると嬉しいです。

最後までお読みいただきありがとうございました!

COMMENT

メールアドレスが公開されることはありません。 が付いている欄は必須項目です