Menu

C#のアクセス修飾子:public、private、protected、internal

C#の6つのアクセス修飾子(public、private、protected、internal、protected internal、private protected)、それぞれが何を許すか、何も書かない場合の既定、そしてそれらが生むコンパイルエラーを解説します。

このページのコードはエディタで実行できます - 編集してすぐに結果を確認できます。

アクセス修飾子は、どのコードが型やメンバーを使えるかを決めます。クラスが、他のコードが頼る部分(公開された面)と、自由に変えられる部分(内部)を分ける方法です。C#には6つの修飾子があり、何も書かない場合の既定もあります。

6つの修飾子

修飾子それを含む型派生型、同じアセンブリその他のコード、同じアセンブリ派生型、別のアセンブリその他のコード、別のアセンブリ
public可可可可可
protected internal可可可可不可
protected可可不可可不可
internal可可可不可不可
private protected可可不可不可不可
private可不可不可不可不可

アセンブリは、コンパイルされた1つのプロジェクト、つまりそれが生成する .dll や .exe です。「同じアセンブリ」は「同じプロジェクト」を意味するので、internal は「自分のコードからは見え、ライブラリの利用者からは隠れる」として働きます。

privateとpublic:カプセル化

private はメンバーの既定で、ほとんどのフィールドに適した選択です。クラスは呼び出し側が必要とするものを public のメソッドやプロパティで公開し、それらのメソッドがオブジェクトのデータの一貫性を保ちます。

出力:

False
True
Priya: 180

balance はprivateなので、それを変更する方法は Deposit と TryWithdraw だけで、どちらもルールを強制します。プログラムの他のどのコードも、どう書かれていようと、負の残高を作れません。これがカプセル化の効果で、ルールが1か所にまとまります。

private はオブジェクトごとではなく型ごとです。Account のメソッドは、別の Account インスタンスの other.balance を読めます。Equals や比較のメソッドは通常この方法で書かれます。

protected:派生クラスから見える

protected のメンバーは外部からは隠されますが、宣言したクラスを継承するクラスからは使えます。

出力:

Sam: 4000.00
Rita: 6400.00

1つ意外なルールがあります。Manager の中では、this や別の Manager の baseSalary は使えますが、任意の Employee のものは使えません。

class Manager : Employee
{
    public decimal Compare(Employee other)
    {
        return baseSalary - other.baseSalary;
        // error CS1540: Cannot access protected member 'Employee.baseSalary' via a qualifier
        // of type 'Employee'; the qualifier must be of type 'Manager' (or derived from it)
    }
}

理由は、other が同じく Employee から派生した Contractor かもしれないからです。protected がアクセスを与えるのは階層の中の自分の枝であって、兄弟の枝ではありません。

protectedなフィールドは、すべての派生クラスをそのフィールドに永遠に結び付けます。多くのコードベースでは、protected なプロパティやメソッドを好むか、フィールドはprivateにして、派生クラスには必要なものだけを与えます。

internal:アセンブリの中で見える

internal のメンバーと型は、同じプロジェクトの任意のコードから使え、その外からは何も使えません。ライブラリは、公開クラスが頼るヘルパーにこれを使います。

// In the MyShop.Pricing library project
public class PriceCalculator
{
    public decimal Total(Cart cart) => TaxRules.Apply(cart.Subtotal);
}

internal static class TaxRules   // callers of the library cannot see this class
{
    internal static decimal Apply(decimal amount) => amount * 1.2m;
}

トップレベルの型は、修飾子を書かなければ internal です。クラスライブラリで public を付け忘れたクラスが、それを参照するプロジェクトから「見当たらない」のはそのためです。

単体テストのプロジェクトも別のアセンブリなので、internal のコードは見えません。標準的な直し方は、ライブラリに属性を付けることです。

[assembly: System.Runtime.CompilerServices.InternalsVisibleTo("MyShop.Pricing.Tests")]

protected internalとprivate protected

この2つは、前の修飾子を逆の方法で組み合わせたもので、名前を取り違えやすいものです。

  • protected internal はprotectedまたはinternalです。同じアセンブリの任意のコードに加えて、別のアセンブリの派生クラスからも使えます。広いほうです。
  • private protected(C# 7.2)はprotectedかつinternalです。同じアセンブリにもある派生クラスからだけ使えます。狭いほうで、拡張ポイントをライブラリの中にとどめたい基底クラスのためのものです。
public class ReportBase
{
    protected internal string Title;      // same assembly, or subclasses anywhere
    private protected int RowLimit = 500; // subclasses in this assembly only (C# 7.2)
}

何も書かない場合の既定

場所既定使えるもの
名前空間で宣言した型internalpublic、internal、file(C# 11)
クラスのメンバーprivate6つすべて
構造体のメンバーprivatepublic、internal、private(構造体は継承できない)
入れ子の型private6つすべて
インターフェースのメンバーpublicC# 8以降は明示的な修飾子も可
enumのメンバーpublicなし

明示的に書くコストは1語で、次に読む人の疑問を取り除けるので、ほとんどのスタイルガイドは、既定と同じであっても修飾子を書くよう求めています。

アクセサーのレベルのアクセス

プロパティやインデクサーは、一方のアクセサーに、プロパティ自体より狭い修飾子を付けられます。

出力:

report.pdf: 100%

アクセサーの修飾子はプロパティのものより制限が強くなければならず、付けられるのは2つのアクセサーのうち一方だけです。

一貫性のないアクセシビリティ

publicなメンバーは、自分より見えにくい型を公開できません。呼び出し側が、名前を書くことを許されていないものを受け取ることになるからです。

internal class Discount { }

public class Checkout
{
    public Discount Current() => null;
    // error CS0050: Inconsistent accessibility: return type 'Discount' is less accessible than method 'Checkout.Current()'
}

同じルールから、引数の型ではCS0051、プロパティの型ではCS0053が出ます。型をメンバーと同じくらい見えるようにするか、メンバーを型と同じくらい隠すことで直します。

修飾子の選び方

動く中で最も制限の強い修飾子から始め、呼び出し側が必要とするときだけ広げます。後から見えるようにするのは簡単で、誰も壊しません。後から見えにくくすると、それを使っていたすべての呼び出し側が壊れます。実際には、フィールドは private、クラスの目的を成すメンバーは public、ヘルパーは private、ライブラリ内だけの型は internal にします。

よくある質問

C#のアクセス修飾子にはどんなものがありますか?

6つあります。public(任意のコード)、private(それを含む型だけ)、protected(それを含む型とそこから派生した型)、internal(同じアセンブリの任意のコード)、protected internal(同じアセンブリ、またはどこにある派生型でも)、private protected(同じアセンブリの派生型、C# 7.2)です。C# 11では、1つのソースファイルでだけ見える型のための file が追加されました。

C#の既定のアクセス修飾子は何ですか?

クラスや構造体のメンバー(フィールド、メソッド、プロパティ、入れ子の型)は既定で private です。トップレベルの型(名前空間に直接宣言したクラス)は既定で internal です。インターフェースのメンバーとenumのメンバーはpublicです。

C#のprotectedとはどういう意味ですか?

protected のメンバーは、自分のクラスの中と、そこから派生したクラスの中で見えますが、それ以外のコードからは見えません。派生クラスがそれを使えるのは自分の型を通してだけです。Manager の中で this.baseSalary は動きますが、別の Employee オブジェクトの baseSalary にアクセスするのはエラーCS1540です。

C#のinternalとは何ですか?

internal は、型やメンバーを、同じアセンブリ(同じプロジェクトのコンパイルされた.dllや.exe)のすべてのコードから見えるようにし、その外からは見えなくします。ライブラリがヘルパークラスを公開APIから外す方法です。[assembly: InternalsVisibleTo("MyLib.Tests")] を使うと、テストプロジェクトからも見えるようになります。

「is inaccessible due to its protection level」はどう直しますか?

これはエラーCS0122で、コードが見ることを許されていないメンバーを使っています。多くの場合、既定の private のままのフィールドです。外部のコードがそれを読むべきなら、公開のプロパティやメソッドを用意します。フィールドをそのままpublicにしてはいけません。別のプロジェクトの型なら、おそらく internal です。

Coddy programming languages illustration

Coddyでコードを学ぼう

始める