C# アクセシビリティとは?public/private/protected/internalの違いと使い分けを初心者向けに解説
はじめに
C#を学び始めると、クラスやメソッドの前に付いているpublic、private、protected、internalといったキーワードをよく見かけます。
C#public class User
{
private string name;
public void SetName(string value)
{
name = value;
}
}
これらは「アクセス修飾子」と呼ばれ、C#のアクセシビリティを決めるために使います。
アクセシビリティとは、簡単にいうと「そのクラスやメソッド、プロパティ、フィールドにどこからアクセスできるか」を決める仕組みです。
C#のアクセシビリティを理解すると、次のようなメリットがあります。
コードの安全性が上がる、余計な変更を防げる、クラスの役割が明確になる、チーム開発で使いやすい設計にできる、ライブラリやAPIの公開範囲を適切に管理できる。
この記事では、C#のアクセシビリティについて、public、private、protected、internalの違いと使い分けを初心者向けにわかりやすく解説します。
1. C#のアクセシビリティとは?
1-1. アクセシビリティは「どこからアクセスできるか」を決める仕組み
C#のアクセシビリティとは、クラス、メソッド、プロパティ、フィールドなどに対して「どこから利用できるか」を制御する仕組みです。
たとえば、あるメソッドをクラスの外から呼び出せるようにしたい場合はpublicを使います。一方で、クラスの内部だけで使いたい変数や処理はprivateにします。
C#public class Calculator
{
public int Add(int a, int b)
{
return a + b;
}
private void Log(string message)
{
Console.WriteLine(message);
}
}
この例では、Addメソッドはpublicなのでクラスの外から呼び出せます。しかし、LogメソッドはprivateなのでCalculatorクラスの内部からしか呼び出せません。
つまりアクセシビリティは、「使ってよい場所」と「使ってはいけない場所」を分けるためのルールです。
1-2. アクセス修飾子とアクセシビリティの関係
アクセシビリティを指定するために使うキーワードがアクセス修飾子です。
代表的なアクセス修飾子には、次のようなものがあります。
publicはどこからでもアクセス可能、privateは同じクラスの内部だけアクセス可能、protectedは同じクラスと継承先クラスからアクセス可能、internalは同じアセンブリ内からアクセス可能です。
C#では、アクセス修飾子を使って「このメンバーは外部に公開する」「このフィールドは内部だけで使う」といった設計を表現します。
C#public class Sample
{
public int PublicValue;
private int PrivateValue;
protected int ProtectedValue;
internal int InternalValue;
}
このように、アクセス修飾子を変えることで、アクセスできる範囲も変わります。
1-3. クラス・メソッド・プロパティ・フィールドに指定できる
C#のアクセシビリティは、さまざまな要素に指定できます。
代表的な対象は、クラス、メソッド、プロパティ、フィールド、コンストラクター、構造体、列挙型、インターフェースなどです。
C#public class User
{
private string name;
public string Name
{
get { return name; }
set { name = value; }
}
public void ShowName()
{
Console.WriteLine(name);
}
}
この例では、Userクラスはpublic、nameフィールドはprivate、NameプロパティとShowNameメソッドはpublicです。
外部からはNameプロパティやShowNameメソッドを通して操作できますが、nameフィールドには直接アクセスできません。
このように、必要な部分だけを公開し、内部の細かい実装は隠すことができます。
1-4. 初心者がまず理解すべき「公開範囲」の考え方
初心者がC#のアクセシビリティを理解するときは、「どこまで公開するか」という考え方が大切です。
すべてをpublicにすれば、確かにどこからでも使えるようになります。しかし、それは安全でわかりやすい設計とはいえません。
たとえば、クラス内部でしか使わないフィールドをpublicにしてしまうと、外部から自由に値を変更できてしまいます。
C#public class User
{
public int Age;
}
この場合、外部から次のように不正な値を入れられてしまいます。
C#User user = new User();
user.Age = -100;
年齢にマイナスの値が入るのは不自然です。こうした問題を防ぐために、フィールドはprivateにして、必要に応じてプロパティやメソッドから操作するようにします。
C#public class User
{
private int age;
public void SetAge(int value)
{
if (value >= 0)
{
age = value;
}
}
}
アクセシビリティは、単なる文法ではなく「安全に使えるコードにするための設計ルール」と考えると理解しやすくなります。
2. C#でアクセシビリティを使う理由
2-1. 不要なアクセスを防いで安全なコードにする
C#でアクセシビリティを使う大きな理由は、不要なアクセスを防ぐためです。
クラスの外から変更してほしくない値や、外部に使わせる必要のない処理を公開してしまうと、意図しない使われ方をされる可能性があります。
C#public class BankAccount
{
public int balance;
}
このように残高をpublicフィールドにしてしまうと、外部から自由に変更できてしまいます。
C#BankAccount account = new BankAccount();
account.balance = -10000;
残高に不正な値を入れられてしまうため、安全ではありません。
そこで、フィールドをprivateにして、入金や出金の処理をメソッドで管理します。
C#public class BankAccount
{
private int balance;
public void Deposit(int amount)
{
if (amount > 0)
{
balance += amount;
}
}
public int GetBalance()
{
return balance;
}
}
このようにすると、外部から直接balanceを書き換えられなくなります。アクセシビリティを適切に使うことで、不正な状態になることを防げます。
2-2. カプセル化によって変更に強い設計にする
アクセシビリティは、オブジェクト指向プログラミングの重要な考え方であるカプセル化と深く関係しています。
カプセル化とは、クラスの内部データや実装を隠し、外部には必要な操作だけを公開する考え方です。
C#public class Product
{
private int price;
public int Price
{
get { return price; }
set
{
if (value >= 0)
{
price = value;
}
}
}
}
この例では、priceフィールドはprivateで隠し、外部からはPriceプロパティを通して操作します。
将来、価格の計算方法や保存方法が変わっても、外部のコードはPriceプロパティを使い続けられます。
つまり、内部の実装を変更しても、外部に与える影響を小さくできます。これが変更に強い設計です。
2-3. チーム開発で使い方を明確にする
チーム開発では、複数人が同じコードを触ります。そのため、「どのメソッドを使ってよいのか」「どのフィールドは直接触ってはいけないのか」が明確であることが重要です。
publicになっているメソッドは、外部から使うことを想定した機能だと判断できます。反対にprivateになっているメソッドは、そのクラス内部の補助的な処理だとわかります。
C#public class OrderService
{
public void CreateOrder()
{
ValidateOrder();
SaveOrder();
}
private void ValidateOrder()
{
// 注文内容を検証する
}
private void SaveOrder()
{
// 注文を保存する
}
}
この例では、外部から使うべきなのはCreateOrderです。ValidateOrderやSaveOrderは内部処理なので、外部から直接呼び出す必要はありません。
アクセス修飾子を適切に付けることで、コードの使い方が自然に伝わります。
2-4. ライブラリやAPIの公開範囲をコントロールする
C#では、ライブラリやAPIを作るときにもアクセシビリティが重要です。
ライブラリを利用する側に公開したいクラスやメソッドはpublicにします。一方で、ライブラリ内部だけで使う補助クラスや処理はinternalやprivateにします。
C#public class CsvReader
{
public void Read()
{
CsvParser parser = new CsvParser();
parser.Parse();
}
}
internal class CsvParser
{
public void Parse()
{
// CSVを解析する内部処理
}
}
この例では、利用者に使ってほしいのはCsvReaderです。CsvParserはライブラリ内部の実装なのでinternalにしています。
公開範囲を適切にコントロールすることで、利用者にとってわかりやすく、保守しやすいライブラリになります。
3. C#の主なアクセス修飾子一覧
3-1. publicとは
publicは、どこからでもアクセスできることを表すアクセス修飾子です。
同じクラス内、別のクラス、別の名前空間、別のアセンブリからでもアクセスできます。
C#public class User
{
public string Name { get; set; }
}
publicは外部に公開したいクラスやメソッド、プロパティに使います。
ただし、何でもpublicにすると外部から自由に使われてしまうため、必要なものだけに限定することが大切です。
3-2. privateとは
privateは、同じクラスの内部からだけアクセスできることを表すアクセス修飾子です。
C#public class User
{
private string password;
}
このpasswordフィールドは、Userクラスの外から直接アクセスできません。
privateは、外部に見せる必要のないフィールドや補助メソッドによく使います。
C#では、クラスのメンバーにアクセス修飾子を書かなかった場合、基本的にprivateになります。
3-3. protectedとは
protectedは、同じクラスの内部と、そのクラスを継承した派生クラスからアクセスできることを表すアクセス修飾子です。
C#public class Animal
{
protected string name;
}
public class Dog : Animal
{
public void ShowName()
{
Console.WriteLine(name);
}
}
この例では、DogクラスはAnimalクラスを継承しているため、protectedのnameにアクセスできます。
protectedは、継承先クラスで使わせたいメンバーに指定します。
3-4. internalとは
internalは、同じアセンブリ内からアクセスできることを表すアクセス修飾子です。
アセンブリとは、簡単にいうとコンパイルされた1つの単位です。多くの場合、1つのプロジェクトから作られる.dllや.exeがアセンブリにあたります。
C#internal class UserRepository
{
public void Save()
{
// 保存処理
}
}
internalにしたクラスは、同じプロジェクト内では使えますが、別のアセンブリからは基本的に使えません。
3-5. protected internalとは
protected internalは、protectedとinternalを組み合わせたアクセス修飾子です。
意味は、「同じアセンブリ内からアクセスできる」または「別アセンブリであっても派生クラスからアクセスできる」です。
C#public class BaseClass
{
protected internal void Show()
{
Console.WriteLine("protected internal");
}
}
protected internalは、protectedよりも広く、internalとも異なる公開範囲を持ちます。
初心者のうちは使用頻度は高くありませんが、ライブラリ設計などで使われることがあります。
3-6. private protectedとは
private protectedは、「同じアセンブリ内にある派生クラスからだけアクセスできる」アクセス修飾子です。
C#public class BaseClass
{
private protected void Show()
{
Console.WriteLine("private protected");
}
}
protected internalが「同じアセンブリ内、または派生クラス」なのに対して、private protectedは「同じアセンブリ内、かつ派生クラス」です。
条件がより厳しいため、公開範囲は狭くなります。
3-7. 省略時のデフォルトアクセシビリティ
C#では、アクセス修飾子を省略した場合にデフォルトのアクセシビリティが適用されます。
トップレベルのクラスは、アクセス修飾子を省略するとinternalになります。
C#class User
{
}
これは次のように書いた場合とほぼ同じ意味です。
C#internal class User
{
}
一方、クラス内のフィールドやメソッドなどのメンバーは、アクセス修飾子を省略するとprivateになります。
C#public class User
{
string name;
void ShowName()
{
Console.WriteLine(name);
}
}
この場合、nameフィールドとShowNameメソッドはprivateとして扱われます。
初心者のうちは、デフォルトに頼らず、publicやprivateを明示的に書くことをおすすめします。
4. publicとは?どこからでもアクセスできる公開範囲
4-1. publicの基本的な意味
publicは、C#のアクセス修飾子の中でもっとも公開範囲が広いものです。
publicを指定したクラス、メソッド、プロパティ、フィールドは、基本的にどこからでもアクセスできます。
C#public class Message
{
public string Text { get; set; }
public void Show()
{
Console.WriteLine(Text);
}
}
このMessageクラスはpublicなので、別のクラスからインスタンスを作成できます。また、TextプロパティやShowメソッドもpublicなので外部から利用できます。
C#Message message = new Message();
message.Text = "Hello";
message.Show();
publicは、外部に使ってもらうことを前提とした機能に使います。
4-2. publicを使う具体例
たとえば、ユーザー情報を表すクラスで、名前を取得・設定できるようにしたい場合は、プロパティをpublicにします。
C#public class User
{
public string Name { get; set; }
public void SayHello()
{
Console.WriteLine($"こんにちは、{Name}さん");
}
}
外部のコードからは次のように使えます。
C#User user = new User();
user.Name = "田中";
user.SayHello();
このように、クラスの利用者に使ってほしい入口をpublicにします。
また、アプリケーションの他の部分から呼び出したいサービスクラスのメソッドもpublicにすることが多いです。
C#public class MailService
{
public void SendMail(string address, string subject, string body)
{
// メール送信処理
}
}
4-3. publicに向いているケース
publicに向いているのは、外部から使う必要があるものです。
たとえば、クラスを利用するためのメソッド、外部から読み書きしたいプロパティ、ライブラリ利用者に公開するAPI、アプリケーション内の他の機能から呼び出す処理などです。
C#public class TaxCalculator
{
public decimal Calculate(decimal price)
{
return price * 1.1m;
}
}
このCalculateメソッドは、税率込み価格を計算するために外部から使うことを想定しているため、publicが適しています。
基本的には、「そのクラスを使う人に呼び出してほしいもの」をpublicにします。
4-4. publicを使いすぎるデメリット
publicは便利ですが、使いすぎると問題が起きやすくなります。
外部から自由にアクセスできる範囲が広がるため、意図しない使われ方をされる可能性があります。また、一度publicにしたメソッドやプロパティは、他のコードから使われている可能性があるため、後から変更しにくくなります。
C#public class User
{
public string password;
}
このようにパスワードのような重要な情報をpublicにすると、外部から直接読み書きできてしまいます。
また、内部処理用のメソッドまでpublicにすると、利用者がどのメソッドを使えばよいのかわかりにくくなります。
C#public class OrderService
{
public void CreateOrder() { }
public void ValidateOrder() { }
public void SaveToDatabase() { }
}
この場合、本来外部から呼び出してほしいのはCreateOrderだけかもしれません。ValidateOrderやSaveToDatabaseが内部処理なら、privateにした方が安全です。
publicは「本当に外部に公開する必要があるものだけ」に使うのが基本です。
5. privateとは?クラス内部だけで使う公開範囲
5-1. privateの基本的な意味
privateは、同じクラスの内部からだけアクセスできるアクセス修飾子です。
外部のクラスからはアクセスできません。継承したクラスからもアクセスできません。
C#public class User
{
private string name;
}
このnameフィールドは、Userクラスの中からは使えますが、外部からは直接使えません。
C#User user = new User();
// user.name = "田中"; // エラー
privateは、クラス内部の状態や補助的な処理を隠すために使います。
5-2. privateを使う具体例
次の例では、balanceフィールドをprivateにしています。
C#public class BankAccount
{
private int balance;
public void Deposit(int amount)
{
if (amount > 0)
{
balance += amount;
}
}
public int GetBalance()
{
return balance;
}
}
外部から直接balanceを変更することはできません。
C#BankAccount account = new BankAccount();
account.Deposit(1000);
Console.WriteLine(account.GetBalance());
このように、privateフィールドをpublicメソッド経由で操作することで、不正な値が入ることを防げます。
5-3. フィールドや補助メソッドをprivateにする理由
フィールドや補助メソッドは、基本的にprivateにすることが多いです。
理由は、クラスの内部実装を外部に見せる必要がないからです。
C#public class ReportService
{
public void CreateReport()
{
LoadData();
FormatData();
PrintReport();
}
private void LoadData()
{
// データ読み込み
}
private void FormatData()
{
// データ整形
}
private void PrintReport()
{
// レポート出力
}
}
外部から使うのはCreateReportだけで十分です。LoadData、FormatData、PrintReportは内部の手順なのでprivateにしています。
補助メソッドをprivateにすることで、クラスの利用者に余計な選択肢を見せずに済みます。
5-4. 初心者がやりがちなprivateの使い忘れ
初心者がよくやりがちなのは、フィールドを何となくpublicにしてしまうことです。
C#public class Player
{
public int hp;
}
このようにすると、外部から自由にhpを変更できます。
C#Player player = new Player();
player.hp = -999;
ゲームのHPがマイナスになるなど、不正な状態を作れてしまいます。
より安全にするなら、フィールドをprivateにして、プロパティやメソッドで制御します。
C#public class Player
{
private int hp;
public int Hp
{
get { return hp; }
private set
{
if (value >= 0)
{
hp = value;
}
}
}
public void Damage(int amount)
{
if (amount > 0)
{
Hp -= amount;
}
}
}
最初は「外部から使わないものはprivate」と考えると、適切な設計に近づきます。
6. protectedとは?継承先クラスから使える公開範囲
6-1. protectedの基本的な意味
protectedは、同じクラスの内部と、そのクラスを継承したクラスからアクセスできるアクセス修飾子です。
C#public class Animal
{
protected string name;
}
nameはAnimalクラスの内部から使えます。また、Animalを継承したクラスからも使えます。
C#public class Dog : Animal
{
public void ShowName()
{
Console.WriteLine(name);
}
}
一方で、継承関係のない外部クラスからはアクセスできません。
C#Dog dog = new Dog();
// dog.name = "ポチ"; // エラー
protectedは、外部には公開したくないけれど、派生クラスには使わせたいメンバーに使います。
6-2. protectedを使う具体例
次の例では、基底クラスCharacterにhpフィールドとTakeDamageメソッドを定義しています。
C#public class Character
{
protected int hp = 100;
protected void TakeDamage(int damage)
{
hp -= damage;
}
}
public class Player : Character
{
public void AttackByEnemy()
{
TakeDamage(10);
Console.WriteLine(hp);
}
}
PlayerクラスはCharacterを継承しているため、protectedのhpやTakeDamageにアクセスできます。
ただし、外部からは直接アクセスできません。
C#Player player = new Player();
// player.hp = 50; // エラー
// player.TakeDamage(10); // エラー
このように、継承先には使わせたいが、外部には公開したくない場合にprotectedを使います。
6-3. privateとの違い
privateとprotectedの違いは、継承先クラスからアクセスできるかどうかです。
privateは同じクラス内からしかアクセスできません。継承先クラスからもアクセスできません。
C#public class Animal
{
private string privateName;
protected string protectedName;
}
public class Dog : Animal
{
public void Test()
{
// privateName = "ポチ"; // エラー
protectedName = "ポチ"; // OK
}
}
同じクラス内だけで使うならprivate、継承先でも使わせたいならprotectedを選びます。
ただし、何でもprotectedにすると派生クラスとの結びつきが強くなります。特に理由がなければ、まずはprivateを選ぶのが基本です。
6-4. protectedを使うべき場面と注意点
protectedを使うべき場面は、継承を前提にした設計をしているときです。
たとえば、基底クラスに共通処理を用意し、派生クラスでその処理を利用したい場合に使います。
C#public abstract class BaseController
{
protected void Log(string message)
{
Console.WriteLine(message);
}
}
public class UserController : BaseController
{
public void CreateUser()
{
Log("ユーザーを作成しました");
}
}
この例では、Logメソッドを派生クラスで使いたいためprotectedにしています。
注意点として、protectedは外部には公開されませんが、派生クラスには公開されます。そのため、基底クラスの内部実装が派生クラスに依存されやすくなります。
継承先で本当に使う必要があるものだけをprotectedにしましょう。
7. internalとは?同じアセンブリ内だけで使える公開範囲
7-1. internalの基本的な意味
internalは、同じアセンブリ内からアクセスできるアクセス修飾子です。
アセンブリが同じであれば、別のクラスからでもアクセスできます。一方で、別のアセンブリからは基本的にアクセスできません。
C#internal class UserRepository
{
public void Save()
{
Console.WriteLine("保存しました");
}
}
このUserRepositoryクラスは、同じアセンブリ内では利用できます。しかし、別のアセンブリからは直接利用できません。
internalは、プロジェクト内部では使いたいけれど、外部には公開したくないクラスやメンバーに向いています。
7-2. アセンブリとは何か
アセンブリとは、C#のコードをコンパイルした結果として作られる単位です。
多くの場合、1つのプロジェクトをビルドすると、.exeや.dllが作られます。この.exeや.dllがアセンブリです。
たとえば、次のような構成を考えます。
MyApp.exe
MyLibrary.dll
MyApp.exeとMyLibrary.dllは別々のアセンブリです。
MyLibrary.dllの中でinternalにしたクラスは、MyLibrary.dllの中からは使えますが、MyApp.exeからは基本的に使えません。
つまりinternalは、「同じプロジェクト内だけで使える」と考えると初心者には理解しやすいです。ただし、厳密にはプロジェクトではなくアセンブリ単位で判断されます。
7-3. internalを使う具体例
ライブラリを作る場合、利用者に公開したいクラスと、内部でだけ使いたいクラスを分けることがあります。
C#public class UserService
{
public void Register()
{
UserValidator validator = new UserValidator();
validator.Validate();
Console.WriteLine("ユーザー登録を行いました");
}
}
internal class UserValidator
{
public void Validate()
{
Console.WriteLine("入力チェックを行いました");
}
}
UserServiceは外部に公開したいのでpublicにしています。一方、UserValidatorはライブラリ内部でだけ使う補助クラスなのでinternalにしています。
このようにすると、ライブラリの利用者はUserServiceだけを意識すればよくなります。
7-4. publicとの違い
publicとinternalの違いは、別アセンブリからアクセスできるかどうかです。
publicは別アセンブリからでもアクセスできます。internalは同じアセンブリ内からのみアクセスできます。
C#public class PublicClass
{
}
internal class InternalClass
{
}
同じアセンブリ内では、どちらのクラスも使えます。
しかし、別のアセンブリから参照した場合、PublicClassは使えますが、InternalClassは使えません。
ライブラリや複数プロジェクト構成のアプリでは、この違いが重要になります。
7-5. internalが使われる実務的なケース
internalは実務でもよく使われます。
たとえば、ライブラリ内部の補助クラス、外部公開したくないサービス、同じプロジェクト内だけで使うユーティリティ、テスト対象の内部実装、APIの裏側で使う変換処理などです。
C#internal class JsonConverter
{
public string Convert(object value)
{
return value.ToString();
}
}
外部に公開する必要はないけれど、同じプロジェクト内の複数クラスから使いたい場合、privateでは狭すぎます。そのようなときにinternalが役立ちます。
internalを使うことで、プロジェクト内部の実装を隠しながら、必要なクラス間では共有できます。
8. public/private/protected/internalの違いを比較
8-1. アクセスできる範囲の比較表
C#の主なアクセス修飾子の違いを表にすると、次のようになります。
| アクセス修飾子 | 同じクラス | 別クラス | 派生クラス | 別アセンブリ |
|---|---|---|---|---|
| public | 〇 | 〇 | 〇 | 〇 |
| private | 〇 | × | × | × |
| protected | 〇 | × | 〇 | 条件付きで〇 |
| internal | 〇 | 〇 | 〇 | × |
ここでいうprotectedの「別アセンブリ」は、派生クラスであればアクセスできる場合があります。ただし、通常の別クラスからはアクセスできません。
初心者はまず、次のように覚えるとわかりやすいです。
publicはどこからでも使える、privateは自分のクラスだけ、protectedは自分のクラスと子クラス、internalは同じアセンブリ内だけです。
8-2. 同じクラスからアクセスできる範囲
同じクラスの中からは、基本的にすべてのアクセス修飾子のメンバーにアクセスできます。
C#public class Sample
{
public int publicValue;
private int privateValue;
protected int protectedValue;
internal int internalValue;
public void Test()
{
publicValue = 1;
privateValue = 2;
protectedValue = 3;
internalValue = 4;
}
}
このように、同じクラス内であればpublic、private、protected、internalのすべてにアクセスできます。
privateは外部からはアクセスできませんが、自分自身のクラス内では自由に使えます。
8-3. 別クラスからアクセスできる範囲
同じアセンブリ内の別クラスからは、publicとinternalにアクセスできます。
C#public class Sample
{
public int publicValue;
private int privateValue;
protected int protectedValue;
internal int internalValue;
}
public class Other
{
public void Test()
{
Sample sample = new Sample();
sample.publicValue = 1;
sample.internalValue = 2;
// sample.privateValue = 3; // エラー
// sample.protectedValue = 4; // エラー
}
}
privateは同じクラス内だけなので、別クラスからはアクセスできません。
protectedも、継承関係のない別クラスからはアクセスできません。
8-4. 継承先クラスからアクセスできる範囲
継承先クラスからは、publicとprotectedにアクセスできます。同じアセンブリ内であればinternalにもアクセスできます。
C#public class BaseClass
{
public int publicValue;
private int privateValue;
protected int protectedValue;
internal int internalValue;
}
public class DerivedClass : BaseClass
{
public void Test()
{
publicValue = 1;
protectedValue = 2;
internalValue = 3;
// privateValue = 4; // エラー
}
}
privateは継承先クラスからもアクセスできません。
「親クラスのメンバーを子クラスでも使いたい」ときは、privateではなくprotectedを検討します。
8-5. 別アセンブリからアクセスできる範囲
別アセンブリから通常アクセスできるのは、基本的にpublicです。
C#public class PublicService
{
public void Execute()
{
}
}
internal class InternalService
{
public void Execute()
{
}
}
別アセンブリから参照した場合、PublicServiceは使えますが、InternalServiceは使えません。
ただし、protectedメンバーは、別アセンブリにある派生クラスからアクセスできる場合があります。
C#public class BaseClass
{
protected void Show()
{
}
}
別アセンブリでこのクラスを継承した場合、派生クラスの内部からShowを呼び出せます。
別アセンブリから利用者に公開したいものはpublic、公開したくない内部実装はinternalにするのが基本です。
9. protected internalとprivate protectedの違い
9-1. protected internalの意味
protected internalは、protectedとinternalを組み合わせたアクセス修飾子です。
意味は、「同じアセンブリ内からアクセスできる、または派生クラスからアクセスできる」です。
C#public class BaseClass
{
protected internal void Show()
{
Console.WriteLine("Show");
}
}
同じアセンブリ内の別クラスからもアクセスできます。
C#public class OtherClass
{
public void Test()
{
BaseClass baseClass = new BaseClass();
baseClass.Show();
}
}
また、別アセンブリであっても、BaseClassを継承したクラスからはアクセスできます。
protected internalは、「同じアセンブリ内」または「派生クラス」というOR条件で考えるとわかりやすいです。
9-2. private protectedの意味
private protectedは、「同じアセンブリ内にある派生クラスからだけアクセスできる」アクセス修飾子です。
C#public class BaseClass
{
private protected void Show()
{
Console.WriteLine("Show");
}
}
このメソッドは、同じアセンブリ内の派生クラスからアクセスできます。
C#public class DerivedClass : BaseClass
{
public void Test()
{
Show();
}
}
しかし、同じアセンブリ内であっても、継承していない別クラスからはアクセスできません。また、別アセンブリの派生クラスからもアクセスできません。
private protectedは、「同じアセンブリ内」かつ「派生クラス」というAND条件で考えると理解しやすいです。
9-3. 2つの違いを比較表で整理
protected internalとprivate protectedの違いを表で整理すると、次のようになります。
| アクセス修飾子 | 同じアセンブリの別クラス | 同じアセンブリの派生クラス | 別アセンブリの派生クラス |
|---|---|---|---|
| protected internal | 〇 | 〇 | 〇 |
| private protected | × | 〇 | × |
protected internalは公開範囲が広く、private protectedは公開範囲が狭いです。
特に重要なのは、protected internalは「同じアセンブリなら継承していなくてもアクセスできる」という点です。
一方、private protectedは「同じアセンブリ内の派生クラスだけ」に限定されます。
9-4. 初心者はまずpublic/private/protected/internalを優先して覚える
protected internalとprivate protectedは、C#のアクセシビリティを細かく制御できる便利な仕組みです。
ただし、初心者が最初から完全に使いこなす必要はありません。
まずは、使用頻度の高い次の4つを優先して覚えましょう。
publicはどこからでもアクセスできる、privateは同じクラス内だけ、protectedは継承先クラスからもアクセスできる、internalは同じアセンブリ内だけです。
この4つを理解できれば、多くのC#コードは読めるようになります。
その後、ライブラリ設計や複数アセンブリ構成を扱うようになった段階で、protected internalとprivate protectedを学ぶとスムーズです。
10. C#のアクセシビリティのデフォルト値
10-1. クラスのデフォルトアクセシビリティ
C#では、トップレベルに定義したクラスのアクセス修飾子を省略すると、デフォルトでinternalになります。
C#class User
{
}
これは次のように書いた場合とほぼ同じ意味です。
C#internal class User
{
}
そのため、別アセンブリからこのUserクラスを使おうとするとアクセスできません。
外部から使う必要があるクラスは、明示的にpublicを付ける必要があります。
C#public class User
{
}
ただし、すべてのクラスをpublicにする必要はありません。外部に公開したいクラスだけをpublicにし、内部実装のクラスはinternalにするのが基本です。
10-2. メンバーのデフォルトアクセシビリティ
クラス内のメンバーは、アクセス修飾子を省略すると基本的にprivateになります。
C#public class User
{
string name;
void ShowName()
{
Console.WriteLine(name);
}
}
この場合、nameフィールドとShowNameメソッドはprivateとして扱われます。
つまり、次のように書いた場合とほぼ同じです。
C#public class User
{
private string name;
private void ShowName()
{
Console.WriteLine(name);
}
}
そのため、外部からShowNameを呼び出そうとするとエラーになります。
C#User user = new User();
// user.ShowName(); // エラー
外部から呼び出したいメソッドには、明示的にpublicを付けましょう。
10-3. 明示的にアクセス修飾子を書くべき理由
C#ではアクセス修飾子を省略できますが、初心者のうちは明示的に書くことをおすすめします。
理由は、コードを読んだときに公開範囲がわかりやすくなるからです。
C#public class User
{
private string name;
public void SetName(string value)
{
name = value;
}
public string GetName()
{
return name;
}
}
このように書くと、nameは内部だけで使うもの、SetNameとGetNameは外部から使うものだとすぐにわかります。
チーム開発でも、アクセス修飾子が明示されていると、他の人がコードの意図を理解しやすくなります。
デフォルト値を知っておくことは大切ですが、実際のコードでは明示的に書く習慣をつけるとよいでしょう。
10-4. デフォルト値で起きやすいエラー
アクセス修飾子の省略によって起きやすいエラーの一つが、「外部から呼び出せると思っていたメソッドにアクセスできない」というものです。
C#public class User
{
void Show()
{
Console.WriteLine("User");
}
}
このShowメソッドは、アクセス修飾子を省略しているためprivateになります。
そのため、外部から呼び出すとエラーになります。
C#User user = new User();
// user.Show(); // エラー
外部から使いたい場合は、publicを付けます。
C#public class User
{
public void Show()
{
Console.WriteLine("User");
}
}
また、クラスのpublicを付け忘れて、別プロジェクトから参照できないというケースもあります。
C#class UserService
{
}
別アセンブリから使いたいなら、次のようにします。
C#public class UserService
{
}
デフォルトアクセシビリティは便利ですが、意図しないアクセス制限につながることもあるため注意しましょう。
11. アクセス修飾子の使い分け方
11-1. 基本はprivateから考える
C#のアクセス修飾子を使い分けるときは、基本的にprivateから考えるのがおすすめです。
つまり、「まずは外部に公開しない」と考え、本当に必要なものだけ公開します。
C#public class Order
{
private int totalPrice;
public void AddItem(int price)
{
if (price > 0)
{
totalPrice += price;
}
}
public int GetTotalPrice()
{
return totalPrice;
}
}
この例では、totalPriceは外部から直接変更されたくないためprivateにしています。
一方で、商品を追加するAddItemや合計金額を取得するGetTotalPriceは外部から使いたいためpublicにしています。
最初から何でもpublicにするのではなく、「必要になるまで公開しない」という考え方が重要です。
11-2. 外部に使わせたいものだけpublicにする
publicは、クラスの利用者に使ってほしいものだけに付けます。
C#public class UserService
{
public void RegisterUser()
{
Validate();
Save();
SendWelcomeMail();
}
private void Validate()
{
}
private void Save()
{
}
private void SendWelcomeMail()
{
}
}
この例では、外部から呼び出してほしいのはRegisterUserだけです。
Validate、Save、SendWelcomeMailは内部処理なのでprivateにしています。
このようにすると、クラスの使い方がシンプルになります。利用者はRegisterUserを呼び出せばよく、内部処理の順番や詳細を意識する必要がありません。
11-3. 継承先で使うものはprotectedにする
継承先クラスで使わせたいメンバーはprotectedにします。
C#public class BaseService
{
protected void Log(string message)
{
Console.WriteLine(message);
}
}
public class UserService : BaseService
{
public void Create()
{
Log("ユーザーを作成しました");
}
}
Logメソッドは外部から直接呼び出す必要はありませんが、派生クラスでは使いたい処理です。このような場合はprotectedが適しています。
ただし、継承先で使う予定がないものまでprotectedにする必要はありません。
迷った場合はprivateにしておき、継承先で必要になったときにprotectedへ変更する方が安全です。
11-4. 同じプロジェクト内だけで使うものはinternalにする
同じアセンブリ内だけで使いたいクラスやメソッドはinternalにします。
C#internal class FileHelper
{
public string ReadText(string path)
{
return File.ReadAllText(path);
}
}
このFileHelperはプロジェクト内部で使う補助クラスとして定義しています。
外部のプロジェクトやライブラリ利用者に公開する必要がない場合、publicではなくinternalにすることで、公開範囲を適切に制限できます。
特に、ライブラリを作るときは、利用者に見せたいクラスだけをpublicにし、内部実装はinternalにするのがよくある設計です。
11-5. 迷ったときの判断基準
アクセス修飾子で迷ったときは、次のように考えると判断しやすくなります。
同じクラス内だけで使うならprivate、外部から使う必要があるならpublic、継承先で使う必要があるならprotected、同じアセンブリ内だけで共有したいならinternalです。
もう少し実践的にいうと、まずprivateにして、本当に外部公開が必要になったらpublicにします。継承設計が明確な場合だけprotectedを使います。プロジェクト内部の実装を隠したい場合はinternalを使います。
公開範囲は広げるより、狭める方が難しいことがあります。
一度publicにしたメソッドを後からprivateにすると、そのメソッドを使っている外部コードが壊れる可能性があります。
そのため、最初はできるだけ狭い公開範囲にしておくのが基本です。
12. アクセシビリティのよくあるエラーと原因
12-1. 「アクセスできない保護レベルです」と表示される原因
C#でアクセシビリティに関する代表的なエラーに、「アクセスできない保護レベルです」というものがあります。
これは、アクセス修飾子によって利用が制限されているメンバーに、アクセスできない場所からアクセスしようとしたときに発生します。
C#public class User
{
private string name;
}
public class Program
{
public void Test()
{
User user = new User();
// user.name = "田中"; // エラー
}
}
nameはprivateなので、Userクラスの外からはアクセスできません。
このような場合は、アクセスしたいメンバーのアクセス修飾子を確認します。
外部から使う必要があるならpublicのプロパティやメソッドを用意します。外部から直接使わせたくないなら、アクセスしようとしている側のコードを修正します。
12-2. privateメンバーに外部からアクセスしている
もっともよくある原因は、privateメンバーに外部からアクセスしているケースです。
C#public class Product
{
private int price;
}
public class Program
{
public void Test()
{
Product product = new Product();
// product.price = 1000; // エラー
}
}
priceはprivateなので、Productクラスの外からはアクセスできません。
この場合、外部から価格を設定したいなら、プロパティを使います。
C#public class Product
{
private int price;
public int Price
{
get { return price; }
set
{
if (value >= 0)
{
price = value;
}
}
}
}
そして、外部からは次のように使います。
C#Product product = new Product();
product.Price = 1000;
フィールドを直接公開するのではなく、プロパティを通して操作することで、安全性を保ちながら外部公開できます。
12-3. internalメンバーに別アセンブリからアクセスしている
internalメンバーに別アセンブリからアクセスしようとした場合もエラーになります。
C#internal class InternalService
{
public void Execute()
{
}
}
このクラスは同じアセンブリ内では使えますが、別のアセンブリからはアクセスできません。
別プロジェクトから参照して使いたい場合は、publicにする必要があります。
C#public class InternalService
{
public void Execute()
{
}
}
ただし、安易にpublicにするのではなく、本当に外部に公開してよいクラスなのかを確認しましょう。
内部処理として隠しておきたいクラスなら、外部から直接使う設計を見直すべきです。
12-4. クラスよりメンバーの公開範囲が広くなっている
C#では、クラスや型の公開範囲とメンバーの公開範囲の関係にも注意が必要です。
たとえば、internalクラスの中にpublicメソッドを書いた場合、そのメソッド自体はpublicですが、クラスがinternalなので別アセンブリからは使えません。
C#internal class UserService
{
public void Register()
{
}
}
この場合、Registerメソッドは同じアセンブリ内では公開されていますが、別アセンブリからはUserService自体が見えないため使えません。
また、publicメソッドの戻り値や引数に、より公開範囲の狭い型を使うとエラーになることがあります。
C#internal class UserData
{
}
public class UserService
{
// public UserData GetUserData() // エラーになる
// {
// return new UserData();
// }
}
UserServiceはpublicなのに、戻り値のUserDataがinternalだと、外部から見たときに型が使えません。
このような場合は、戻り値の型をpublicにするか、公開するメソッドの設計を見直します。
12-5. エラーを解消するための確認ポイント
アクセシビリティ関連のエラーが出たときは、まずアクセスしようとしている場所と、アクセスされる側の修飾子を確認します。
同じクラス内からアクセスしているのか、別クラスからアクセスしているのか、継承先クラスからアクセスしているのか、別アセンブリからアクセスしているのかを整理しましょう。
次に、アクセスされる側がprivate、protected、internal、publicのどれになっているかを確認します。
外部から使うべきメンバーならpublicにします。クラス内部だけで使うべきメンバーなら、外部からアクセスしないようにコードを修正します。継承先で使いたいならprotectedにします。同じアセンブリ内だけで共有したいならinternalにします。
また、クラス自体のアクセシビリティと、メソッドの戻り値や引数の型のアクセシビリティにも注意しましょう。
エラーが出たからといって、すぐにすべてをpublicにするのは避けるべきです。必要な範囲だけを公開することが大切です。
13. 初心者向けのアクセシビリティ設計のコツ
13-1. フィールドはprivate、外部公開はプロパティを使う
初心者がまず意識したいのは、フィールドは基本的にprivateにすることです。
C#public class User
{
private string name;
public string Name
{
get { return name; }
set { name = value; }
}
}
外部から値を読み書きしたい場合は、フィールドを直接publicにするのではなく、プロパティを使います。
C#では、自動実装プロパティを使って簡潔に書くこともできます。
C#public class User
{
public string Name { get; set; }
}
ただし、値のチェックを入れたい場合は、フィールドをprivateにしてプロパティで制御します。
C#public class User
{
private int age;
public int Age
{
get { return age; }
set
{
if (value >= 0)
{
age = value;
}
}
}
}
このように、外部公開にはプロパティを使うことで、値の検証や将来の変更に対応しやすくなります。
13-2. メソッドは必要最小限だけpublicにする
メソッドは、必要最小限だけpublicにしましょう。
C#public class PaymentService
{
public void Pay()
{
Validate();
Charge();
SendReceipt();
}
private void Validate()
{
}
private void Charge()
{
}
private void SendReceipt()
{
}
}
この例では、外部から呼び出すのはPayだけです。支払い処理の内部手順であるValidate、Charge、SendReceiptはprivateにしています。
すべてのメソッドをpublicにすると、利用者がどのメソッドを呼べばよいのかわかりにくくなります。
また、内部処理の順番を間違えて呼び出される可能性もあります。
publicメソッドは、クラスの利用者に提供する正式な入口と考えましょう。
13-3. 継承を前提にしないならprotectedを増やしすぎない
protectedは、継承先クラスからアクセスできる便利な修飾子です。
しかし、継承を前提にしていないクラスでprotectedを増やしすぎると、設計が複雑になります。
C#public class Service
{
protected void Step1()
{
}
protected void Step2()
{
}
}
このようにすると、派生クラスが内部処理に依存しやすくなります。
将来、Step1やStep2の処理を変更したくなっても、派生クラスで使われている可能性があるため、簡単に変更できなくなります。
継承先で本当に必要なものだけをprotectedにし、それ以外はprivateにするのが基本です。
継承を使う予定がないなら、無理にprotectedを使う必要はありません。
13-4. internalはプロジェクト内の実装隠蔽に使う
internalは、プロジェクト内では共有したいけれど、外部には公開したくないものに使います。
C#internal class CacheManager
{
public void Clear()
{
}
}
このCacheManagerは、同じアセンブリ内の複数クラスから使えます。しかし、別アセンブリには公開されません。
ライブラリを作るときは、外部利用者に見せたいクラスだけをpublicにし、内部で使うクラスはinternalにします。
C#public class ImageService
{
private readonly ImageLoader loader = new ImageLoader();
public void ShowImage()
{
loader.Load();
}
}
internal class ImageLoader
{
public void Load()
{
}
}
このようにすると、ライブラリの利用者にはImageServiceだけが見え、ImageLoaderは内部実装として隠せます。
13-5. 後から変更しやすい公開範囲を選ぶ
アクセシビリティを決めるときは、後から変更しやすいかどうかも考えましょう。
一般的に、公開範囲を広げる変更は比較的しやすいです。たとえば、privateをpublicにする変更は、既存の外部コードを壊しにくいです。
一方で、公開範囲を狭める変更は影響が大きくなりがちです。publicだったメソッドをprivateにすると、そのメソッドを使っていた外部コードがコンパイルできなくなる可能性があります。
そのため、最初は必要最小限の公開範囲にしておくことが大切です。
基本方針は、フィールドはprivate、外部に使わせたい操作だけpublic、継承先に使わせたいものだけprotected、同じアセンブリ内だけで使うものはinternalです。
この考え方を守ると、変更に強く、安全で読みやすいコードになります。
14. C#のアクセシビリティに関するよくある質問
14-1. publicとinternalの違いは何ですか?
publicとinternalの違いは、別アセンブリからアクセスできるかどうかです。
publicは、別アセンブリからでもアクセスできます。ライブラリの利用者に公開したいクラスやメソッドに使います。
C#public class PublicService
{
}
internalは、同じアセンブリ内からだけアクセスできます。外部には公開したくない内部実装に使います。
C#internal class InternalService
{
}
同じプロジェクト内だけで使いたいならinternal、別プロジェクトやライブラリ利用者にも使わせたいならpublicを選びます。
14-2. privateとprotectedの違いは何ですか?
privateとprotectedの違いは、継承先クラスからアクセスできるかどうかです。
privateは、同じクラスの内部からだけアクセスできます。継承先クラスからもアクセスできません。
C#public class BaseClass
{
private int privateValue;
}
protectedは、同じクラスの内部と、継承先クラスからアクセスできます。
C#public class BaseClass
{
protected int protectedValue;
}
public class DerivedClass : BaseClass
{
public void Test()
{
protectedValue = 10;
}
}
同じクラス内だけで使うならprivate、派生クラスでも使うならprotectedです。
ただし、迷った場合はまずprivateにするのがおすすめです。
14-3. classにprivateは指定できますか?
トップレベルのクラスには、基本的にprivateを指定できません。
C#// private class User // エラー
// {
// }
トップレベルのクラスに指定できる代表的なアクセス修飾子は、publicとinternalです。
C#public class PublicUser
{
}
internal class InternalUser
{
}
ただし、クラスの中に定義する入れ子クラスにはprivateを指定できます。
C#public class OuterClass
{
private class InnerClass
{
}
}
この場合、InnerClassはOuterClassの内部からだけ使えます。
14-4. アクセス修飾子を省略するとどうなりますか?
アクセス修飾子を省略すると、C#のデフォルトアクセシビリティが適用されます。
トップレベルのクラスは、アクセス修飾子を省略するとinternalになります。
C#class User
{
}
クラス内のメンバーは、アクセス修飾子を省略すると基本的にprivateになります。
C#public class User
{
string name;
void Show()
{
}
}
このnameフィールドとShowメソッドはprivateとして扱われます。
省略しても動作しますが、初心者のうちはpublicやprivateを明示的に書く方が、コードの意図を理解しやすくなります。
14-5. 初心者はどのアクセス修飾子から覚えるべきですか?
初心者は、まずpublic、private、protected、internalの4つを覚えるのがおすすめです。
最初に覚えるべきなのは、publicとprivateです。
publicは外部から使える、privateは同じクラス内だけで使える、という違いを理解しましょう。
次に、継承を学ぶタイミングでprotectedを覚えます。
最後に、複数プロジェクトやライブラリ開発を学ぶときにinternalを理解するとよいです。
最初からすべてを完璧に覚える必要はありません。まずは「基本はprivate、外部に公開したいものだけpublic」という考え方を身につけることが大切です。
まとめ
C#のアクセシビリティとは、クラス、メソッド、プロパティ、フィールドなどに対して「どこからアクセスできるか」を決める仕組みです。
アクセシビリティを指定するために、public、private、protected、internalなどのアクセス修飾子を使います。
publicはどこからでもアクセスできる公開範囲です。外部に使ってほしいクラスやメソッド、プロパティに使います。
privateは同じクラスの内部からだけアクセスできる公開範囲です。フィールドや補助メソッドなど、外部に見せる必要のないものに使います。
protectedは同じクラスと継承先クラスからアクセスできる公開範囲です。派生クラスで使わせたいメンバーに使います。
internalは同じアセンブリ内からだけアクセスできる公開範囲です。プロジェクト内部では共有したいけれど、外部には公開したくないクラスやメンバーに使います。
さらに、protected internalは「同じアセンブリ内、または派生クラス」からアクセスでき、private protectedは「同じアセンブリ内の派生クラス」からアクセスできます。
初心者がC#のアクセシビリティを使い分けるときは、まずprivateを基本に考えましょう。そして、外部から使わせたいものだけpublicにし、継承先で必要なものはprotected、同じアセンブリ内だけで使うものはinternalにします。
アクセシビリティを適切に設定すると、不要なアクセスを防ぎ、安全で変更に強いコードを書けるようになります。
C#でクラス設計をするときは、「このメンバーは本当に外部に公開する必要があるか」を意識しながら、アクセス修飾子を選ぶことが大切です。

