C#のプロパティは、クラスのデータを安全かつわかりやすく扱うための仕組みです。
中でも{ get; set; }
は、値の取得と代入を定義する基本形で、初心者にとって最初に覚えるべき重要な構文です。
本記事では、プロパティの基本、書き方、実用パターン、初期化と活用まで、やさしく段階的に解説します。
C#のプロパティとは { get; set; } の基本
プロパティの役割とフィールドの違い
プロパティは、クラスの内部データ(フィールド)へのアクセス方法を提供する「窓口」です。
直接フィールドを公開すると、予期しない値の代入や整合性の崩れが起こりやすくなります。
そこでプロパティで受け口を定義し、必要なら検証や変換を挟むことで、安全かつ意図が伝わるAPIになります。
以下の表は、フィールドとプロパティの違いを簡潔にまとめたものです。
項目 | フィールド | プロパティ |
---|---|---|
役割 | 値そのものの保管場所 | 値の取得/設定のインターフェース |
アクセス制御 | 直接アクセスになりやすい | get/setで制御や検証が可能 |
互換性 | リファクタリングで破綻しやすい | 実装の変更に強い(内部の保持方法を変えやすい) |
シリアライゼーション | 種類により挙動が変わる | 属性で制御しやすい |
命名 | _camelCaseなど | PascalCase(例: Name, Age) |
基本方針として、外部に公開するのはプロパティ、内部の格納はフィールドと覚えておくとよいです。
get と set の意味
get
: 値を取得する処理です。呼び出し側から見ると「読み取り」です。set
: 値を代入するときの処理です。代入前に検証や正規化を行えます。
手動でフィールドを用意してプロパティを構築する例を示します。
// 手動実装プロパティの例
public class Person
{
// バッキングフィールド(backing field)
private string _name = "Unknown";
// プロパティ
public string Name
{
get
{
// 取得時にそのまま返す例
return _name;
}
set
{
// 代入時にnullや空文字を許さない
if (string.IsNullOrWhiteSpace(value))
{
throw new ArgumentException("Nameは空にできません。");
}
_name = value.Trim();
}
}
}
初心者が押さえる使い方のコツ
プロパティは文法が簡潔ですが、目的は「安全で意図が明確なデータアクセス」です。
最初は自動実装プロパティから始め、必要になったら手動実装に切り替えるのがおすすめです。
外部公開はプロパティ、内部データはフィールドという棲み分けを守ると、拡張や保守が楽になります。
自動実装プロパティの書き方
基本の宣言 { get; set; } の使い方
自動実装プロパティは、バッキングフィールドを自動生成してくれる構文です。
簡潔に書けるため、最初の一歩に最適です。
using System;
public class Person
{
// 自動実装プロパティ(バッキングフィールドはコンパイラが自動生成)
public string Name { get; set; }
public int Age { get; set; }
}
public class Program
{
public static void Main()
{
var p = new Person();
p.Name = "Taro";
p.Age = 20;
Console.WriteLine($"Name: {p.Name}, Age: {p.Age}");
}
}
Name: Taro, Age: 20
既定値の初期化
C# 6以降は、プロパティに初期値を直書きできます。
コンストラクタを書かずにデフォルトを設定でき、初学者にも扱いやすいです。
public class Account
{
public string Owner { get; set; } = "Guest"; // 既定値
public decimal Balance { get; set; } = 0m; // 既定値
}
using System;
public class Program
{
public static void Main()
{
var a = new Account();
Console.WriteLine($"Owner: {a.Owner}, Balance: {a.Balance}");
var b = new Account { Owner = "Hanako", Balance = 1500m };
Console.WriteLine($"Owner: {b.Owner}, Balance: {b.Balance}");
}
}
Owner: Guest, Balance: 0
Owner: Hanako, Balance: 1500
プロパティ名の命名ルール
プロパティ名はPascalCaseにします(例: Name
, CreatedAt
)。
内部のバッキングフィールドは_camelCaseが一般的です(例: _name
)。
この統一は可読性を大きく高めます。
get と set の実用パターン
値を取得する get
計算結果を返す「読み取り専用」プロパティは便利です。
フィールドを持たず、他プロパティから派生した値をまとめて返せます。
using System;
public class Rectangle
{
public double Width { get; set; }
public double Height { get; set; }
// 計算プロパティ(読み取り専用)
public double Area
{
get
{
return Width * Height; // 面積を毎回計算して返す
}
}
}
public class Program
{
public static void Main()
{
var r = new Rectangle { Width = 3, Height = 4 };
Console.WriteLine($"Area: {r.Area}");
r.Width = 10;
Console.WriteLine($"Area after resize: {r.Area}");
}
}
Area: 12
Area after resize: 40
値を代入する set
代入時の検証(バリデーション)はsetで行います。
入力をTrim
したり範囲をチェックして、不正値を拒否できます。
using System;
public class Product
{
private string _name = "Unnamed";
private decimal _price;
public string Name
{
get => _name;
set
{
if (string.IsNullOrWhiteSpace(value))
throw new ArgumentException("Nameは空白不可です。");
_name = value.Trim(); // 余分な空白を除去
}
}
public decimal Price
{
get => _price;
set
{
if (value < 0m)
throw new ArgumentOutOfRangeException(nameof(value), "Priceは0以上にしてください。");
_price = value;
}
}
}
public class Program
{
public static void Main()
{
var p = new Product();
p.Name = " Coffee "; // setでTrim
p.Price = 250m;
Console.WriteLine($"{p.Name} - {p.Price}円");
}
}
Coffee - 250円
簡単なチェックで不正値を防ぐ
最小限のチェックでも効果は大きいです。
例えば年齢を0以上に制限するなど、ドメインの前提条件をプロパティで守ると、後工程での不具合を減らせます。
public class Member
{
private int _age;
public int Age
{
get => _age;
set
{
// 不正値を受け取らない
if (value < 0) throw new ArgumentOutOfRangeException(nameof(value), "Ageは0以上です。");
_age = value;
}
}
}
読み取り専用にする private set
外部からは読み取りだけ許し、クラス内部だけが更新できるようにするにはprivate set
を使います。
更新タイミングを制御したい「現在状態」や「更新日時」に適します。
using System;
public class Order
{
public string Number { get; }
public DateTime CreatedAt { get; } = DateTime.UtcNow;
// 外部からは読み取り専用、内部では更新可能
public DateTime LastUpdated { get; private set; } = DateTime.UtcNow;
public Order(string number)
{
Number = number ?? throw new ArgumentNullException(nameof(number));
}
public void UpdateSomething()
{
// 何らかの更新処理...
LastUpdated = DateTime.UtcNow; // 内部でのみ更新
}
}
public class Program
{
public static void Main()
{
var o = new Order("A-001");
Console.WriteLine($"CreatedAt: {o.CreatedAt:O}");
Console.WriteLine($"LastUpdated: {o.LastUpdated:O}");
o.UpdateSomething();
Console.WriteLine($"LastUpdated(after): {o.LastUpdated:O}");
}
}
CreatedAt: 2025-09-12T03:55:03.0855286Z
LastUpdated: 2025-09-12T03:55:03.0855512Z
LastUpdated(after): 2025-09-12T03:55:03.0884391Z
public setの乱用は意図しない変更につながります。
必要がなければprivate setやgetのみで最小公開にしましょう。
プロパティの初期化と活用例
クラスの作り方とインスタンス生成での流れ
C#ではクラス(class)にプロパティを定義し、newでインスタンス(オブジェクト)を生成して使います。
初期値はプロパティ初期化子、コンストラクタ、オブジェクト初期化子のいずれかで与えられます。
状況に応じて使い分けるのがポイントです。
コンストラクタでプロパティを初期化
必須項目がある場合、コンストラクタで初期化を強制します。
こうすると「必ず埋まっている」という前提を保てます。
using System;
public class User
{
// 必須項目はgetのみ(読み取り専用)にし、コンストラクタで設定
public string Id { get; }
public string Name { get; private set; }
// 任意項目は既定値を設定しておく
public string Email { get; set; } = "unset@example.com";
public User(string id, string name)
{
// 簡単な引数チェック
if (string.IsNullOrWhiteSpace(id)) throw new ArgumentException("Idは必須です。");
if (string.IsNullOrWhiteSpace(name)) throw new ArgumentException("Nameは必須です。");
Id = id.Trim();
Name = name.Trim();
}
public void Rename(string newName)
{
if (string.IsNullOrWhiteSpace(newName)) throw new ArgumentException("Nameは空にできません。");
Name = newName.Trim();
}
}
public class Program
{
public static void Main()
{
var u = new User("u-100", "Sakura");
Console.WriteLine($"{u.Id}: {u.Name}, {u.Email}");
u.Rename("Sakura Tanaka");
Console.WriteLine($"{u.Id}: {u.Name}, {u.Email}");
}
}
u-100: Sakura, unset@example.com
u-100: Sakura Tanaka, unset@example.com
オブジェクト初期化子でまとめて設定
プロパティをまとめて初期化するにはオブジェクト初期化子
が便利です。
読みやすく、初期化コードを簡潔にできます。
using System;
using System.Collections.Generic;
public class Book
{
public string Title { get; set; } = "Untitled";
public string Author { get; set; } = "Unknown";
public int Year { get; set; } = 0;
}
public class Program
{
public static void Main()
{
// オブジェクト初期化子で一括設定
var b1 = new Book { Title = "C#入門", Author = "Yamada", Year = 2025 };
var b2 = new Book { Title = "やさしいOOP", Author = "Sato" }; // Yearは既定値
// コレクション初期化子と併用する例
var shelf = new List<Book>
{
new Book { Title = "LINQ実践", Author = "Kato", Year = 2024 },
b1,
b2
};
foreach (var b in shelf)
{
Console.WriteLine($"{b.Title} by {b.Author} ({b.Year})");
}
}
}
LINQ実践 by Kato (2024)
C#入門 by Yamada (2025)
やさしいOOP by Sato (0)
ヒント: 既定値(初期化子)とオブジェクト初期化子を組み合わせると、必要な部分だけを上書きでき、コードの意図が明確になります。
どの初期化方法を選ぶべきかの目安
- 必須項目: コンストラクタで要求する(未設定を防ぐ)。
- 任意項目: プロパティの既定値でカバーし、必要に応じて上書き。
- 多数の任意項目を一度に設定: オブジェクト初期化子で簡潔に。
一貫した初期化方針をチーム内で決めておくと、保守が楽になります。
まとめ
本記事では、C#のプロパティ { get; set; }を、役割の理解から自動実装、検証を伴う実用パターン、そして初期化の実践まで解説しました。
外部公開はプロパティ、内部保持はフィールドという原則を守り、必要に応じてprivate set
や計算プロパティを活用すると、堅牢で読みやすいコードになります。
まずは自動実装プロパティでシンプルに始め、要件が増えたら手動実装や検証を組み込んでいきましょう。