2026/05/11

C# 2026年のプロパティ宣言——fieldキーワードとget setの書き方まとめ

C# 2026年のプロパティ宣言——fieldキーワードとget/setの書き方まとめ

C#のプロパティ宣言は、バージョンを追うごとにどんどん短く書けるようになっている。古いコードを読んでいると、バッキングフィールドを自分で宣言してgetとsetでそれを返すだけ、という10行近いボイラープレートが普通に出てくる。2026年時点の書き方と比べると、同じことをやっているのに別の言語みたいに見える。

この記事では、C# 14(.NET 10)までの機能を踏まえて、2026年時点でプロパティをどう書くべきかを整理した。特にfieldキーワードは実際に使い始めたら手放せなくなったので、そこを中心に書いている。

古い書き方——バッキングフィールドを自分で管理する

昔からあるパターンはこれだ。バリデーションや副作用が必要なプロパティは、プライベートフィールドを自分で宣言して、getとsetの中で操作する。

public class User
{
    private string _name;

    public string Name
    {
        get => _name;
        set
        {
            if (string.IsNullOrWhiteSpace(value))
                throw new ArgumentException("名前は空にできません");
            _name = value;
        }
    }
}

動くし読めるけど、_nameという変数がNameプロパティのためだけに存在していて、それが当たり前のように積み重なっていく。クラスが大きくなると、どのフィールドがどのプロパティに対応しているかを目で追う必要が出てくる。

C# 14のfieldキーワード——バッキングフィールドが不要になる

.NET 10(C# 14)で追加されたfieldキーワードを使うと、コンパイラが生成するバッキングフィールドにアクセサの中から直接アクセスできる。上のコードは次のように書き直せる。

public class User
{
    public string Name
    {
        get => field;
        set
        {
            if (string.IsNullOrWhiteSpace(value))
                throw new ArgumentException("名前は空にできません");
            field = value;
        }
    }
}

_nameの宣言が消えた。fieldはコンパイラが自動生成するバッキングフィールドへの参照で、外から直接触れない。オートプロパティとカスタムアクセサの中間点みたいな位置づけで、「単純に値を返すだけじゃないけど、専用のフィールドを宣言するほどでもない」ケースにちょうど合う。

片方だけカスタムにすることもできる

setだけロジックを入れて、getはそのまま返す、という使い方が実際には多い。

public string Email
{
    get;
    set
    {
        if (!value.Contains('@'))
            throw new FormatException("メールアドレスの形式が不正です");
        field = value.ToLowerInvariant();
    }
}

getはオートプロパティのまま、setだけカスタムにできる。これがfieldキーワードの使い方としていちばん出番が多いんじゃないかと思っている。

initアクセサ——オブジェクト初期化時だけ書き込みを許可する

C# 9で追加されたinitは、コンストラクタやオブジェクト初期化子から設定できるけど、その後は変更できないプロパティを作れる。setの代わりにinitと書くだけだ。

public class Order
{
    public Guid Id { get; init; } = Guid.NewGuid();
    public DateTime CreatedAt { get; init; } = DateTime.UtcNow;
    public string CustomerId { get; init; }
}

// 初期化子でセット可能
var order = new Order { CustomerId = "C001" };

// これはコンパイルエラー
order.CustomerId = "C002";

レコード型と組み合わせることが多いけど、普通のクラスで「作ったあとは変えない値」を持ちたいときにも使える。

requiredモディファイア——初期化を強制する

C# 11で入ったrequiredは、オブジェクト初期化子でそのプロパティを設定しないとコンパイルエラーにしてくれる。

public class Product
{
    public required string Name { get; init; }
    public required decimal Price { get; init; }
    public string Description { get; init; }  // 任意
}

// Nameを省略するとコンパイルエラー
var p = new Product { Price = 1000 };  // エラー: Nameが設定されていない

// 正しい使い方
var p = new Product { Name = "コーヒー豆", Price = 1000 };

コンストラクタを書かなくてもプロパティの初期化漏れを防げる。DTO・モデルクラスで特に重宝する。

プライマリコンストラクタ——C# 12で普通のクラスにも

C# 12でレコード型だけでなく通常のクラスにもプライマリコンストラクタが使えるようになった。DIでよくある「コンストラクタでサービスを受け取ってフィールドに代入する」パターンが短く書ける。

// 以前
public class OrderService
{
    private readonly IOrderRepository _repository;
    private readonly ILogger _logger;

    public OrderService(IOrderRepository repository, ILogger logger)
    {
        _repository = repository;
        _logger = logger;
    }
}

// C# 12以降
public class OrderService(IOrderRepository repository, ILogger logger)
{
    public async Task GetAsync(Guid id)
    {
        logger.LogInformation("Getting order {Id}", id);
        return await repository.GetByIdAsync(id);
    }
}

フィールド宣言が消えてコンストラクタも消えた。パラメータはクラス全体のスコープで使える。ただし、プライマリコンストラクタのパラメータはプロパティではないので、外から参照させたい場合は別途プロパティとして定義する必要がある点は注意だ。

2026年時点での使い分け

整理するとこうなる。

パターン使う場面
{ get; set; }単純なプロパティ。バリデーションなし
{ get; init; }初期化後は変更させたくないプロパティ
required { get; init; }初期化必須 + 変更不可。DTOやモデル
fieldキーワードsetにバリデーション・変換が必要。バッキングフィールドは不要
バッキングフィールド手動宣言フィールドを他のメソッドからも参照する場合

fieldキーワードが追加されたことで、手動バッキングフィールドが必要なケースはかなり限られてきた。大半のケースはfieldで書けるし、その方が宣言が少なくてクラスがスッキリする。

まとめ

  • C# 14のfieldキーワードでバッキングフィールド宣言が不要になった。setのバリデーションにfield = valueと書くだけ
  • init(C# 9)で初期化後に変更不可のプロパティを宣言できる
  • required(C# 11)でプロパティの初期化漏れをコンパイル時に検出できる
  • プライマリコンストラクタ(C# 12)でDIのボイラープレートを大幅に削減できる

2026/05/08

C# WPFのデータバインディングで日本語プロパティ名を使ったら何が起きるか

C# WPFのデータバインディングで日本語プロパティ名を使ったら何が起きるか

「C#って日本語の変数名が使えるらしい」という話は聞いたことがあった。でも実際にWPFのデータバインディングで試したことはなかった。{Binding 名前}みたいに書いて本当に動くのか。動いたとして、実用に耐えるのか。半分好奇心、半分「どうせ落とし穴があるだろう」という気持ちで検証してみた。

そもそもC#は日本語識別子を正式にサポートしている

C#の言語仕様(ECMA-334)では、識別子にUnicode文字を使えると明記されている。つまり変数名・プロパティ名・クラス名に漢字・ひらがな・カタカナをそのまま使えるのは仕様として保証された挙動で、バグでも裏技でもない。

// これは正式に有効なC#コード
public class 利用者
{
    public string 名前 { get; set; }
    public int 年齢 { get; set; }
    public string メールアドレス { get; set; }
}

Visual Studio 2022でも普通にIntelliSenseが効く。補完候補に「名前」「年齢」が出てくる様子はなかなかシュールだけど、ビルドは通る。

WPFのXAMLバインディングでも使えるか

ViewModelに日本語プロパティを持たせてXAMLからバインドしてみた。

// ViewModel
public class 利用者ViewModel : INotifyPropertyChanged
{
    private string _名前;
    public string 名前
    {
        get => _名前;
        set
        {
            _名前 = value;
            OnPropertyChanged(nameof(名前));
        }
    }

    private int _年齢;
    public int 年齢
    {
        get => _年齢;
        set
        {
            _年齢 = value;
            OnPropertyChanged(nameof(年齢));
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;
    protected void OnPropertyChanged(string name)
        => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}
<!-- XAML -->
<StackPanel>
    <TextBox Text="{Binding 名前, UpdateSourceTrigger=PropertyChanged}" />
    <TextBox Text="{Binding 年齢, UpdateSourceTrigger=PropertyChanged}" />
    <TextBlock Text="{Binding 名前}" />
</StackPanel>

結果は——動く。{Binding 名前}でちゃんとViewModelの名前プロパティにバインドされて、TextBoxに入力した値がリアルタイムで反映される。WPFのバインディングは内部でリフレクションを使ってプロパティ名を文字列で解決しているので、Unicode名でも問題ない。

nameof()も日本語プロパティで機能する

INotifyPropertyChangedの実装でnameof(名前)と書けるのが地味に重要で、プロパティ名のタイポをコンパイル時に検出できる。ハードコードで"名前"と文字列を渡す実装より安全だ。

// nameof()は日本語プロパティでも正しく機能する
OnPropertyChanged(nameof(名前)); // → "名前" という文字列になる

実際に困った点

IME切り替えのストレス

コードを書くとき、英字と日本語の間で頻繁にIMEを切り替えることになる。public string まで英字で書いて、プロパティ名を入力するために日本語入力に切り替えて、また { get; set; }に戻す、という繰り返しが思ったより煩わしい。慣れの問題かもしれないけど、英語コードの中に日本語が挟まるリズムが崩れる感覚は否めない。

バックエンドとの連携がつらい

APIのレスポンスをViewModelにマッピングするとき、JSONのキーは英語("name""age")でプロパティは日本語、という状況になる。System.Text.JsonやNewtonsoft.Jsonで属性を付ければ対応できるけど、一段階手間が増える。

using System.Text.Json.Serialization;

public class 利用者
{
    [JsonPropertyName("name")]
    public string 名前 { get; set; }

    [JsonPropertyName("age")]
    public int 年齢 { get; set; }
}

StackOverflowやGitHub Copilotとの相性

英語のプロパティ名なら、同じ構造のコードがStackOverflowにいくらでもある。日本語プロパティ名のコードは検索してもほぼ出てこないし、Copilotの補完も精度が落ちる。サンプルコードをそのまま流用しにくい。

使ってみた正直な感想

「動く」と「実用的」は別の話だと改めて思った。一人で書く小さなツールで、日本語のビジネスドメインを扱う場合は選択肢としてアリかもしれない。「顧客番号」「請求金額」「納期」みたいな用語が英訳すると意味が曖昧になるドメインでは、日本語のまま書いた方がコードと仕様書の対応がはっきりする、という考え方はわかる。

ただチームで使う場合や、外部のAPIやライブラリとの連携が多い場合は苦労の方が大きいと思う。英語識別子のコードベースに日本語が混ざると、どちらのルールで書けばいいか迷う人が出てくる。「愚行」と言い切るほどではないけど、採用するなら明確な理由と運用ルールが必要だ。

まとめ

  • C#は仕様としてUnicode識別子をサポートしており、日本語プロパティ名は正式に有効
  • WPFのデータバインディング({Binding 名前}形式)も日本語プロパティで正常に動作する
  • nameof()INotifyPropertyChanged・XAMLバインディングすべて問題なく機能した
  • 実用上の課題はIME切り替え、JSON属性の追加、英語サンプルコードとの乖離
  • ドメイン用語が日本語で完結する一人プロジェクトなら選択肢になりうる。チーム開発では要検討