2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【C#】修飾子一覧まとめ(アクセス修飾子・メンバー修飾子・継承修飾子・パラメータ修飾子)

2
Last updated at Posted at 2026-03-05

はじめに

C#の修飾子について調べた内容の備忘録です。
学習中に調べた内容を自分用にまとめます。

internalって範囲どこまでだっけ?
overrideする時ってvirtualじゃなきゃダメなんだっけ?
と記憶が定着していなかったため、修飾子についてまとめて一覧を作成する。

修飾子クイックリファレンス

修飾子    目的 実務での使用例
public 外部公開 UseCase の Execute、公開 API
private カプセル化 フィールド・内部メソッド
protected 継承用公開 基底クラスのフック
internal アセンブリ限定 同一アセンブリのユーティリティ
sealed 継承禁止 Entity・UseCase 全般
static インスタンス不要 Factory・Helper メソッド
readonly 不変フィールド DI注入値・依存オブジェクト
const コンパイル時定数 アプリ設定・定数値
virtual オーバーライド許可(実装あり) 共通処理+カスタマイズ余地がある基底クラス
abstract 実装強制(実装なし) 基底 UseCase・テンプレートメソッド
override 親メソッドを置き換え 継承先での実装・上書き
async 非同期処理 DB・ファイル IO
partial クラス分割 WinForms Designer ファイル
ref 双方向の参照渡し 呼び出し元変数を直接変更したい場面
out 出力専用の参照渡し TryXxx パターン・複数の戻り値
in 読み取り専用参照渡し 大きな struct の効率渡し

分類と詳細

C# の修飾子は以下の 4 種類に分類できる。

分類 修飾子
アクセス修飾子 public / private / protected / internal
メンバー修飾子 static / const / readonly / async / partial
継承修飾子 virtual / abstract / override / sealed
パラメータ修飾子 ref / out / in

アクセス修飾子

「どこからアクセスできるか」を制御する。
基本は private とし、必要なものだけ公開範囲を広げる。

修飾子 意味 実務用途
public どこからでもアクセス可能 外部公開API
private 同一クラスのみ フィールド・実装
protected 継承クラスのみ 継承用メンバー
internal 同一アセンブリのみ 同一アセンブリ内限定
protected internal 同一アセンブリ OR 継承先 フレームワーク拡張ポイント
private protected 同一アセンブリ AND 継承先 基底クラスの内部拡張

補足
protected internal = protected OR internal
private protected = protected AND internal

実務の基本ルール

基本は private
同一アセンブリ内限定は internal
外部公開するものだけ public
継承用は protected

省略時のデフォルト

修飾子を省略した場合、最も制限の強いアクセス修飾子が自動的に適用される。

対象 省略時のデフォルト
クラス・インターフェース(トップレベル) internal
メンバー(フィールド・メソッド等) private
class Counter { }          // → internal class Counter と同じ
internal class Counter { } // → 明示的に書いた場合(推奨)

internal class Product
{
    int _price;            // → private int _price と同じ
}

実務原則: 省略より明示的に書くほうが意図が伝わりやすため、推奨

記述例

public class Product
{
    public string Name;       // 外部からアクセス可能
    private int _price;       // クラス内部のみ
    protected int Id;         // 継承先のみ
    internal string Code;     // 同一アセンブリのみ
}

メンバー修飾子

クラス、メンバーの振る舞いや性質を制御する。

static 修飾子

インスタンスを生成せずに使用できるメンバー・クラスを定義する。

static クラス

// static クラス(インスタンス化不可)
static class MathHelper
{
    public static double Pi = 3.14;
    public static double Calc() => Pi;
}

static フィールド(全インスタンス共有)

static フィールドはクラスに属し、全インスタンスで値を共有する。

class Counter
{
    public static int Total = 0;  // 全インスタンス共有
    public int Id;                // インスタンスごと

    public Counter()
    {
        Total++;    // インスタンスが生成されるたびに共有カウントが増える
        Id = Total; // 自分の ID は生成時の Total を使う
    }
}

// 動作確認
var a = new Counter();  // Total=1, a.Id=1
var b = new Counter();  // Total=2, b.Id=2
var c = new Counter();  // Total=3, c.Id=3

Console.WriteLine(Counter.Total);  // → 3(クラス名でアクセス)
Console.WriteLine(a.Id);           // → 1(インスタンスごとに異なる)
Console.WriteLine(b.Id);           // → 2

ポイント
Total はどのインスタンスからアクセスしても同じ値。Id はインスタンスごとに独立。

実務用途

Utility クラス、Factory メソッド、Helper クラスに使用する。

// Entity の Factoryメソッド
public static Product Create(ProductId id, ProductName name, Money price)
{
    return new Product(id, name, price);
}

const / readonly 修飾子

項目 const readonly
決定タイミング コンパイル時(固定値のみ) 実行時でも OK
代入できる場所 宣言時のみ 宣言時 + コンストラクタ内
暗黙的 static ○(インスタンス不要) ×(インスタンスに属する)
使える型 プリミティブ・string のみ 任意の型(クラス・構造体も可)
推奨用途 固定の文字列・数値定数 DI注入値・依存オブジェクト

const の使用例

固定値が決まっている定数に使用する。コンパイル時に値が埋め込まれるため、参照アセンブリ側に変更が伝わらない点に注意。

public static class AppConstants
{
    public const string AppName = "QualitySystem";  // コンパイル時定数
    public const int MaxRetry   = 3;
    public const double Pi      = 3.14159;
}

// 使用:インスタンス不要
Console.WriteLine(AppConstants.AppName);

readonly の使用例

実行時に値が確定する場合やクラス・構造体を不変で持ちたい場合、コンストラクタで依存を受け取る場合に使用する。

public class ProductService
{
    private readonly ProductRepository _repository;

    public ProductService(ProductRepository repository)
    {
        _repository = repository;
    }
}

使い分けの判断基準

コンパイル時に値が確定する固定値  → const
  例)ステータスコード、アプリ名、上限値の定数

実行時に確定する・クラス型を使う → readonly
  例)Entity の ID、コンストラクタで受け取る依存オブジェクト

❌ const では使えないケース
    public const DateTime Limit = DateTime.Now;  // NG: 実行時値は使えない
    public const ProductId Id   = new ProductId(); // NG: クラス型は使えない

async 修飾子

DB アクセス・ファイル処理などの I/O 待機処理に使用する。
asyncawait は常にセットで使用する。(await が無い場合は警告になる)

await演算子(operator)。非同期処理の完了を待機して結果を取得するためのキーワード。

public async Task<string> FetchAsync()
{
    // await で結果を直接受け取る
    return await httpClient.GetStringAsync(url);
}

public async Task Execute()
{
    // 完了を待つ
    await Task.Delay(1000);
}

partial 修飾子

クラスを複数ファイルに分割できる。
WinForms の Designer ファイルで自動使用されている。

// FrmMain.cs(ロジック)
public partial class FrmMain : Form
{
    // 自分で書くコード
}

// FrmMain.Designer.cs(自動生成)
public partial class FrmMain
{
    // Visual Studio が自動生成するコード
}

継承修飾子

クラスやメソッドの継承・拡張方法を制御する。

virtual(親クラス)

サブクラスでオーバーライド可能なメソッドを定義する。
デフォルト実装を持ち、override は任意。

public class ReportBase
{
    public virtual void Print()
    {
        Console.WriteLine("基本レポート");  // 実装あり
    }
}

// 子クラスは override してもしなくても OK
public class DetailReport : ReportBase
{
    public override void Print()
    {
        Console.WriteLine("詳細レポート");  // 上書き
    }
}

abstract(抽象)

実装なし、override を強制する。
クラス、メソッド共に abstract にしなければならない。
インスタンス化不可。継承先で実装しないとコンパイルエラーになる。

public abstract class UseCase
{
    public abstract void Execute();  // 実装なし、継承先で必ず実装
}

public class SaveProductUseCase : UseCase
{
    public override void Execute()         // 実装しないとコンパイルエラー
    {

    }
}

virtual abstract比較

virtual abstract
実装 あり(デフォルト動作) なし
override 任意 強制(しないとエラー)
インスタンス化 ×
用途 共通処理+カスタマイズ余地 実装を強制したいテンプレート

override(子クラス)

親クラスの virtual または abstract メソッドを置き換える。
親の実装は完全に上書きされるが、base.メソッド() で親の処理を呼び出すことも可能。

public class DetailReport : ReportBase
{
    public override void Print()
    {
        base.Print();                         // 親の処理を呼ぶ(任意)
        Console.WriteLine("詳細レポート追加");  // 追加処理
    }
}

var report = new DetailReport();
report.Print();
// → 基本レポート
// → 詳細レポート追加

継承した場合 と override した場合のコード例

// 親クラス
public class ReportBase
{
    //virtualでoverride可能にする
    public virtual void Print()
    {
        Console.WriteLine("基本レポート");
    }
}

// override なし(親のメソッドをそのまま使う)
public class ReportA : ReportBase { }

// override あり(親のメソッドを上書き)
public class ReportB : ReportBase
{
    public override void Print()
    {
        Console.WriteLine("ReportB のレポート");
    }
}

// base あり(親の処理を呼んだうえで追加)
public class ReportC : ReportBase
{
    public override void Print()
    {
        base.Print();
        Console.WriteLine("ReportC の追加情報");
    }
}

new ReportA().Print();  // → 基本レポート         (親のまま)
new ReportB().Print();  // → ReportB のレポート   (完全に上書き)
new ReportC().Print();  // → 基本レポート         (親 → 子の順)
                        // → ReportC の追加情報

sealed(継承禁止)

クラスやメソッドの 継承・オーバーライドを禁止する。
設計上の意図しない拡張を防ぎ、クラスの振る舞いを固定する目的で使用する。
Entity・UseCase への使用を推奨。

public sealed class SaveProductUseCase  // このクラスは継承できない
{
    // ...
}

// override メソッドに sealed を付けると、それ以上のメソッドの継承だけ禁止
public class Base
{
    public virtual void Execute() { }
}
public class Child : Base
{
    public sealed override void Execute() { }  // これ以上のオーバーライド不可
}

パラメータ修飾子

メソッド引数の値の渡し方を制御する。

通常のメソッド引数は値渡し(コピーが渡される)だが、ref / out / in を使うと参照渡しになる。

比較表

ref out in
渡す前の初期化 必須 不要 必須
メソッド内での代入 任意 必須 NG(読み取り専用)
読み取り ○(代入後)
書き込み ×
主な用途 双方向の値変更 複数の戻り値 大きな構造体の効率渡し

ref(読み書き可能)

呼び出し元の変数を直接変更します。渡す前に必ず初期化が必要です。

void Swap(ref int a, ref int b)
{
    int tmp = a;
    a = b;
    b = tmp;
}

// 呼び出し側にも ref が必要
int x = 10, y = 20;
Swap(ref x, ref y);
Console.WriteLine(x);  // → 20
Console.WriteLine(y);  // → 10

使う場面
使う場面は限定的。戻り値で返せるなら戻り値を優先する。


out(出力専用)

メソッドから複数の値を返すために使います。渡す前の初期化は不要ですが、メソッド内で必ず代入する必要があります。

// 標準ライブラリの典型例
bool ok = int.TryParse("123", out int result);
// ok     → true
// result → 123

// 自分で定義する場合
bool TryGetUser(int id, out string name, out string email)
{
    if (id == 1)
    {
        name  = "田中";
        email = "tanaka@example.com";
        return true;
    }
    name  = "";   // out は必ず代入が必要
    email = "";
    return false;
}

// 使用
if (TryGetUser(1, out string name, out string email))
{
    Console.WriteLine(name);   // → 田中
}

// 不要な out 引数はディスカード(_)で無視できる
bool ok2 = int.TryParse("abc", out _);

使う場面
TryXxx パターンで処理の成否(bool)+結果値を同時に返したい場面で使う。
Resultでいいような気がする。


in(読み取り専用参照)

参照渡しだが変更不可
大きな struct をコピーせずに渡す際のパフォーマンス最適化が主目的。

struct BigData
{
    public double A, B, C, D;  // 大きな構造体
}

void Process(in BigData data)
{
    Console.WriteLine(data.A);  // 読み取りのみ OK
    // data.A = 1.0;            // NG: コンパイルエラー
}

var d = new BigData { A = 1.0 };
Process(in d);  // コピーなしで渡せる(呼び出し側の in は省略可)

使う場面
通常のクラス(参照型)には効果なし。大きな struct(値型)を扱う場面で検討する。


まとめ

C# の修飾子について整理しました。
適切な修飾子を付けることで、コードの公開範囲や設計意図が明確になり、コードを読む人にも意図が伝わりやすくなると思います。

修飾子に迷ったときは「何のために使うか」を意識すると整理しやすい。

  • 公開範囲を絞る → アクセス修飾子(基本は private、必要な分だけ広げる)
  • 値を守る → readonly / const
  • 継承を制御する → virtual / abstract / sealed
  • 振る舞いを変える → static / async / partial

実務ではまず private / internal / public を使い分け、
継承が必要な場合のみ protected を検討するのが基本。

private protected は基底クラスの拡張でたまに使用し、
protected internal は実務ではほとんど使わない。

DIで受け取る依存オブジェクトなど、値を変更させたくないフィールドには readonly を使用し、固定値には const を使用する。

オーバーライド可能にしたい場合は virtual
オーバーライドを強制したい場合は abstract
継承させたくないクラスには sealed を使用する。

この記事は自分用の備忘録ですが、同じく C# を学習している方の参考になれば嬉しいです。

2
2
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?