TECH MEDIA

テックメディア


プログラミング言語
ブログ

C#におけるメンバ変数とプロパティの違い

C#
目次
  1. 01|はじめに
  2. 02|値が書き換わるときに追加の処理を実行できるかどうか
  3. 03|値型を格納するときに、コンパイルエラーに気を付ける必要があるかどうか
  4. 04|おわりに

1. はじめに

こんにちは、ショートスリーパーに憧れるエンジニアの糸賀です。

メンバ変数とプロパティってどう違うんだろう。幾千の星の光が輝く乾いた夜空を見上げながら、ふと疑問に感じたので調べてみました。ということで、今回の主題はC#におけるメンバ変数とプロパティの違いについてです。
一言で表すとメソッドを介すかどうかです。これではわかりづらいと思いますのでもう少し具体的に説明します。以下の2点です。

  • 値が書き換わるときに、追加の処理を実行できるかどうか
  • 値型を格納するときに、コンパイルエラーに気を付ける必要があるかどうか

C#バージョン: 5.0
最終更新日: 2021/5/30

2. 値が書き換わるときに追加の処理を実行できるかどうか

プロパティを使うと、ゲッターとセッターの内部に処理を追加できます。値を書き換えるときに、プロパティのセッターが呼ばれ、例1のようにオブジェクトの都合のいいように値を調整してプロパティ値としてその値を保持できます。
対して、メンバ変数の場合は単に値が書き換わって終了です。つまり、メンバ変数はその値が書き換えられたとき、オブジェクトはその変更に何も対応できないということです。
それがプロパティであれば、”その値を変えんの?あ、ちょっと待って。この処理してからね。はい、OK。”というように、その値を変えるならこの処理しておきたいんだよーという場合にプロパティが役立ちます。

例1

int _number;
public int Number
{
	get
	{
		return this._number / 2;
	}
	set
	{
		this._number = value * 2;
	}
}

3. 値型を格納するときに、コンパイルエラーに気を付ける必要があるかどうか

例2プロパティは内部的に例3のようにメンバ変数とゲッター、セッターに置き換えられます。

例2

public class Item
{
	public Coordinate Loc { get; set; }
}

例3

public class Item
{
	private Coordinate _Loc;
	public Coordinate get_Loc()
	{
		return _Loc;
	}
	public void set_Loc(Coordinate value)
	{
		_Loc= value;
	}
}

これは言い換えると、プロパティの値を読み書きするときにはメソッドが呼ばれていることになります。
ここで一つ厄介なことがあります。そのゲッターの戻り値の型(=プロパティの型)が参照型ではなく、値型の場合は、コンパイルエラーが発生するケースがあります。メソッドは戻り値の型が値型の場合は、参照ではなく値そのものを返します。したがって、その値の内部にアクセスする場合は、その値を入れる変数を用意する必要があります。

わかりにくいので例4をご覧ください。”item.Loc.X = 100;”に注目してください。プロパティ値”Loc”の型は”struct”なので値型です。そして”Loc”はプロパティなので、この処理では”Loc”のセッターが呼ばれています。したがって、“item.Loc”は値を返します。この時、返しているのは変数ではなく値です。言い換えると行き場のない値が宙に浮いている状態です。もちろん変数ではなく、値そのものを返しているので”item.Loc.X”を参照できずコンパイルエラーとなります。例5のようになれば問題ないです。これはプロパティがメソッドだから起きる問題であり、すでに変数であるメンバ変数の場合はこのようなことは起こりません。

例4

class Program
{
	static void Main(string[] args)
	{
		var item = new Item();
		item.Loc.X = 100;
	}
}

public Coordinate Item
{
	public Coordinate Loc { get; set; }
}

public struct Coordinate
{
	public int X { get; set; }
	public int Y { get; set; }
}

例5

class Program
{
	static void Main(string[] args)
	{
		var item = new Item();
		var coordinate = item.Loc;
		coordinate.X = 100;
	}
}

4. おわりに

プロパティはメンバ変数の読み書きをメソッドを呼ぶことで実現しているので、処理が追加できる反面、メソッドを呼ぶことから生じるややこしさがあります。メンバ変数はその逆で読み書きは単純ですが、値が書き換わったことをオブジェクト自身が検知できません(=値が書き換わった時の処理が書けない)。

しかしながら、個人的にはよっぽどのことでない限り、プロパティを使用するほうがいいのではと思います。プロパティは記述すること自体のコストが低く、可読性も高い上に、メンバ変数の値が好き勝手変わるという事象も防げます。

値型のプロパティを扱う際にコンパイルエラーが生じ得るというデメリットも発生頻度は低いでしょうし、IntelliSenseが賢いので直ぐに気付いて修正できます。以上のことから私が言えるのは、たまには夜空を見上げて思索にふけってみてはいかがでしょうかということです。

参考文献

Compiler Error CS1612
https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/compiler-messages/cs1612

Griffiths, I., ed. Programming C# 5.0: Building Windows 8, Web, and Desktop Applications for the .NET 4.5 Framework O’Reilly Media, Inc.: California

RECRUIT 採用情報

「eビジネスに関わる全ての人を幸せにする」
私達とともに新たな時代をつくりませんか?