bucket-sort logo bucket-sort

プログラミングとインフラエンジニアリングの覚え書き

  • Posts
  • About
  • Contact
  1. Home
  2. All Posts
  3. [C#] プロパティ応用編 — init・required・抽象プロパティ・インデクサまで

[C#] プロパティ応用編 — init・required・抽象プロパティ・インデクサまで

Apr 21, 2026 C# , .NET bucket-sort

前回の記事ではプロパティの基本構文を整理しました。本記事では init アクセサ、required 修飾子、抽象・仮想プロパティ、インターフェースプロパティ、インデクサなど、応用的な機能をまとめます。

init アクセサ(C# 9.0〜)

init は set の代わりに使えるアクセサで、オブジェクト初期化子での設定のみを許可します。初期化後は変更できない「初期化時限定の書き込み」を実現します。

public class Person
{
    public string Name { get; init; }
    public int Age { get; init; }
}

// オブジェクト初期化子で設定 — OK
var p = new Person { Name = "Alice", Age = 30 };

// 初期化後の変更 — コンパイルエラー
// p.Name = "Bob";

set と init の違い

特徴 set init
コンストラクタ内 ✅ ✅
オブジェクト初期化子 ✅ ✅
初期化後の代入 ✅ ❌
不変性の保証 なし 初期化後は不変

init は record 型との相性が良く、イミュータブルなデータモデルを作る際に重宝します。

init アクセサでバリデーション

init にも set と同様にロジックを記述できます。

public class Product
{
    private string _sku = "";

    public string Sku
    {
        get => _sku;
        init
        {
            if (string.IsNullOrWhiteSpace(value))
                throw new ArgumentException("SKU は空にできません。");
            _sku = value;
        }
    }
}

var product = new Product { Sku = "ABC-123" }; // OK
// var bad = new Product { Sku = "" };          // ArgumentException

required 修飾子(C# 11〜)

required を付けたプロパティは、オブジェクト初期化子での設定が必須になります。コンストラクタのパラメータなしに「設定漏れ」を防げます。

public class User
{
    public required string Email { get; set; }
    public required string DisplayName { get; set; }
    public string? Bio { get; set; } // こちらは任意
}

// Email と DisplayName は必ず設定する必要がある
var user = new User
{
    Email = "alice@example.com",
    DisplayName = "Alice"
};

// Email を省略するとコンパイルエラー
// var bad = new User { DisplayName = "Bob" };

required と init の組み合わせ

required と init を組み合わせると「初期化時に必ず設定し、以降は変更不可」という制約を表現できます。

public class Order
{
    public required string OrderId { get; init; }
    public required DateTime CreatedAt { get; init; }
    public string? Note { get; set; }
}

var order = new Order
{
    OrderId = "ORD-001",
    CreatedAt = DateTime.UtcNow,
    Note = "急ぎ"
};

// order.OrderId = "ORD-002"; // コンパイルエラー(init のため変更不可)

コンストラクタで required を満たす

コンストラクタ内でプロパティを設定する場合、SetsRequiredMembers 属性を付けることで required の制約をコンストラクタが満たしていることを宣言できます。

using System.Diagnostics.CodeAnalysis;

public class User
{
    public required string Email { get; set; }
    public required string DisplayName { get; set; }

    public User() { }

    [SetsRequiredMembers]
    public User(string email, string displayName)
    {
        Email = email;
        DisplayName = displayName;
    }
}

// コンストラクタ経由 — required の警告なし
var user = new User("alice@example.com", "Alice");

抽象プロパティ(abstract)

abstract プロパティは基底クラスでシグネチャだけを宣言し、派生クラスで実装を強制します。

public abstract class Shape
{
    public abstract double Area { get; }
    public abstract string Name { get; }
}

public class Circle : Shape
{
    public double Radius { get; }

    public Circle(double radius) => Radius = radius;

    public override double Area => Math.PI * Radius * Radius;
    public override string Name => "Circle";
}

public class Square : Shape
{
    public double Side { get; }

    public Square(double side) => Side = side;

    public override double Area => Side * Side;
    public override string Name => "Square";
}

abstract プロパティは抽象クラス(abstract class)内でのみ宣言でき、派生クラスは override で実装を提供する必要があります。

仮想プロパティ(virtual / override)

virtual プロパティはデフォルト実装を持ちつつ、派生クラスで上書き可能にします。

public class Animal
{
    public virtual string Sound => "...";
}

public class Dog : Animal
{
    public override string Sound => "Woof!";
}

public class Cat : Animal
{
    public override string Sound => "Meow!";
}

Animal animal = new Dog();
Console.WriteLine(animal.Sound); // Woof!

インターフェースプロパティ

インターフェースにプロパティを定義すると、実装クラスにプロパティの提供を強制できます。

public interface IIdentifiable
{
    string Id { get; }
}

public interface ITimestamped
{
    DateTime CreatedAt { get; }
    DateTime? UpdatedAt { get; set; }
}

public class Article : IIdentifiable, ITimestamped
{
    public string Id { get; } = Guid.NewGuid().ToString();
    public DateTime CreatedAt { get; } = DateTime.UtcNow;
    public DateTime? UpdatedAt { get; set; }
}

デフォルト実装(C# 8.0〜)

C# 8.0 以降、インターフェースプロパティにもデフォルト実装を持たせることができます。

public interface IVersioned
{
    int Version => 1; // デフォルト実装
}

public class Document : IVersioned
{
    // Version を実装しなくてもコンパイル可能
    // IVersioned 経由でアクセスするとデフォルト値 1 が返る
}

IVersioned doc = new Document();
Console.WriteLine(doc.Version); // 1

ただしデフォルト実装はインターフェース型の参照を通じてのみアクセス可能です。Document 型の変数から直接 Version を呼ぶことはできません。

インデクサ(Indexer)

インデクサはオブジェクトに [](インデックス演算子)でアクセスできるようにする特殊なプロパティです。this[...] で定義します。

public class Sentence
{
    private string[] _words;

    public Sentence(string text)
    {
        _words = text.Split(' ');
    }

    // インデクサ
    public string this[int index]
    {
        get => _words[index];
        set => _words[index] = value;
    }

    public int Length => _words.Length;

    public override string ToString() => string.Join(" ", _words);
}

var sentence = new Sentence("Hello World Today");
Console.WriteLine(sentence[0]); // Hello
sentence[1] = "C#";
Console.WriteLine(sentence);    // Hello C# Today

文字列キーのインデクサ

インデクサの引数は int に限りません。string やその他の型も使えます。

public class HttpHeaders
{
    private readonly Dictionary<string, string> _headers = new(StringComparer.OrdinalIgnoreCase);

    public string? this[string name]
    {
        get => _headers.TryGetValue(name, out var value) ? value : null;
        set
        {
            if (value is null)
                _headers.Remove(name);
            else
                _headers[name] = value;
        }
    }
}

var headers = new HttpHeaders();
headers["Content-Type"] = "application/json";
headers["Authorization"] = "Bearer token123";
Console.WriteLine(headers["content-type"]); // application/json(大文字小文字を無視)

複数パラメータのインデクサ

インデクサには複数のパラメータを定義することもできます。

public class Matrix
{
    private readonly double[,] _data;

    public Matrix(int rows, int cols)
    {
        _data = new double[rows, cols];
    }

    public double this[int row, int col]
    {
        get => _data[row, col];
        set => _data[row, col] = value;
    }
}

var m = new Matrix(2, 2);
m[0, 0] = 1.0;
m[0, 1] = 2.0;
m[1, 0] = 3.0;
m[1, 1] = 4.0;
Console.WriteLine(m[1, 1]); // 4

プロパティの各機能と対応バージョン

機能 C# バージョン
プロパティ基本構文 1.0
自動実装プロパティ 3.0
自動実装プロパティの初期値 6.0
get のみの自動実装プロパティ 6.0
Expression-Bodied プロパティ 6.0(get)/ 7.0(get + set)
インターフェースのデフォルト実装 8.0
init アクセサ 9.0
required 修飾子 11

まとめ

  • init アクセサはオブジェクト初期化子での設定のみを許可し、初期化後の変更を防ぐ
  • required 修飾子でプロパティの設定漏れをコンパイル時に検出できる
  • required + init で「必須かつ不変」なプロパティを定義できる
  • 抽象プロパティで派生クラスに実装を強制、仮想プロパティでデフォルト実装を提供できる
  • インターフェースプロパティで契約としてプロパティを要求でき、C# 8.0 からはデフォルト実装も可能
  • インデクサで [] を使った直感的なアクセスを独自クラスに追加できる
C# .NET プロパティ Init Required インデクサ
← [C#] プロパティ(Properties)を整理する — 基本構文から自動実装・計算プロパティまで [C#] オブジェクト初期化を整理する — new() からオブジェクト初期化子・init・required・with 式まで →

Related Posts

  • [C#] オブジェクト初期化を整理する — new() からオブジェクト初期化子・init・required・with 式まで Apr 22, 2026
  • [C#] プロパティ(Properties)を整理する — 基本構文から自動実装・計算プロパティまで Apr 20, 2026
  • [C#] Finalizable & Disposable パターン実践 — Dispose パターンの完全形 May 13, 2026
  • [C#] Disposable Objects — IDisposable / Dispose() と using 構文 May 12, 2026

Table of Contents

  • init アクセサ(C# 9.0〜)
    • set と init の違い
    • init アクセサでバリデーション
  • required 修飾子(C# 11〜)
    • required と init の組み合わせ
    • コンストラクタで required を満たす
  • 抽象プロパティ(abstract)
  • 仮想プロパティ(virtual / override)
  • インターフェースプロパティ
    • デフォルト実装(C# 8.0〜)
  • インデクサ(Indexer)
    • 文字列キーのインデクサ
    • 複数パラメータのインデクサ
  • プロパティの各機能と対応バージョン
  • まとめ

Recent Posts

  • [C#] Finalizable & Disposable パターン実践 — Dispose パターンの完全形 May 13, 2026
  • [C#] Disposable Objects — IDisposable / Dispose() と using 構文 May 12, 2026
  • [C#] Finalizable Objects — Finalize() の役割と使いどころ May 11, 2026
  • [C#] System.GC クラスを整理する — ガベージコレクションを制御するための API May 10, 2026
  • [C#] IComparable と IComparer — オブジェクトの順序比較と複数ソート戦略 May 9, 2026

Categories

  • C#63
  • .NET62
  • AWS27
  • Laravel16
  • Linux15
  • MySQL9
  • Apache8
  • PHP8
  • DynamoDB6
  • セキュリティ6
  • Nginx5
  • WordPress4
  • インフラ4
  • Hugo3
  • .NET Framework1
  • Aurora1
  • Filament1
  • Git1
  • SQS1

Tags

  • C#
  • .NET
  • AWS
  • Laravel
  • PHP
  • セキュリティ
  • MySQL
  • Linux
  • Apache
  • Code Snippet
  • DynamoDB
  • NoSQL
  • PHP-FPM
  • RDS
  • パフォーマンス
  • DoS
  • Nginx
  • Windows
  • WordPress
  • メモリ管理
  • 監視
  • 設計
  • Amazon Linux 2023
  • Docker
  • IDisposable
  • Ipset
  • Iptables
  • OPCache
  • Webサーバー
  • オブジェクト指向
  • クラス設計
  • コレクション
  • デザインパターン
  • パターンマッチング
  • 継承
  • 認可
  • Aurora
  • Blade
  • Grafana
  • Hugo
  • InfluxDB
  • Policy
  • Record
  • SSG
  • インターフェース
  • エラーハンドリング
  • カプセル化
  • ガベージコレクション
  • モニタリング
  • 例外
Powered by Hugo & Explore Theme.