C#のinternalとprivateの違いとは?アクセス修飾子の使い分けを初心者向けに解説
はじめに
C#でクラスやメソッド、フィールドを定義していると、internalやprivateというアクセス修飾子をよく見かけます。どちらも「外部から自由にアクセスさせないための指定」ですが、アクセスできる範囲が大きく異なります。
結論から言うと、privateは「同じクラスの中だけ」で使える最も閉じた指定です。一方、internalは「同じアセンブリ内」、初心者向けに言い換えると多くの場合「同じプロジェクト内」からアクセスできる指定です。MicrosoftのC#リファレンスでも、privateは包含している型にアクセスを制限し、internalは現在のアセンブリにアクセスを制限すると説明されています。Microsoft Learn+1
この記事では、C#のinternalとprivateの違いを、初心者にもわかりやすいようにコード例付きで解説します。アクセス修飾子の基本から、実務での使い分け、よくあるエラー、protected internalやprivate protectedとの違いまで順番に確認していきましょう。
1. C#のinternalとprivateの違いを最初に結論で理解しよう
C#のinternalとprivateの違いは、アクセスできる範囲です。
privateは、宣言したクラスや構造体の中からだけアクセスできます。別のクラスからは、たとえ同じファイル内に書かれていてもアクセスできません。
internalは、同じアセンブリ内からアクセスできます。Visual Studioや.NETのプロジェクトで考えると、多くの場合は「同じプロジェクト内の別クラスからアクセスできる」と理解するとイメージしやすいです。
つまり、より制限が強いのはprivateです。より広い範囲で共有できるのがinternalです。
1-1. internalは「同じアセンブリ内」からアクセスできる
internalは、同じアセンブリ内のコードからアクセスできるアクセス修飾子です。公式ドキュメントでも、internalな型やメンバーは同じアセンブリ内のファイルからのみアクセスできると説明されています。Microsoft Learn
C#internal class UserService
{
internal void CreateUser()
{
Console.WriteLine("ユーザーを作成しました");
}
}
このUserServiceクラスはinternalなので、同じアセンブリ内であれば別のクラスから利用できます。
C#class Program
{
static void Main()
{
var service = new UserService();
service.CreateUser();
}
}
同じプロジェクト内であれば、このようにアクセスできます。ただし、別のプロジェクトから参照した場合は、基本的にアクセスできません。
1-2. privateは「同じクラス内」からのみアクセスできる
privateは、同じクラスや構造体の内部からのみアクセスできるアクセス修飾子です。公式ドキュメントでも、privateは包含している型にアクセスを制限すると定義されています。Microsoft Learn+1
C#class User
{
private string name;
private void SetName(string value)
{
name = value;
}
public void UpdateName(string value)
{
SetName(value);
}
}
この例では、nameフィールドとSetNameメソッドはprivateです。そのため、Userクラスの外側から直接アクセスすることはできません。
C#var user = new User();
// user.name = "Taro"; // エラー
// user.SetName("Taro"); // エラー
privateは、クラス内部の処理や状態を外部から隠したいときに使います。
1-3. internalとprivateの違いを表で比較
| 比較項目 | internal | private |
|---|---|---|
| アクセスできる範囲 | 同じアセンブリ内 | 同じクラス、構造体などの内部 |
| 別クラスからのアクセス | 同じアセンブリ内なら可能 | 不可 |
| 別プロジェクトからのアクセス | 基本的に不可 | 不可 |
| 主な用途 | プロジェクト内だけで共有したい処理 | クラス内部だけで使う処理 |
| 制限の強さ | 中程度 | 強い |
| 初心者向けのイメージ | 同じプロジェクト内用 | そのクラス専用 |
internalは「外部プロジェクトには公開したくないが、プロジェクト内では使いたいもの」に向いています。
privateは「そのクラスの中だけで完結させたいもの」に向いています。
1-4. 初心者がまず押さえるべき使い分けの基準
初心者は、まず次の基準で使い分けると理解しやすいです。
| 判断基準 | 使うアクセス修飾子 |
|---|---|
| そのクラスの中だけで使う | private |
| 同じプロジェクト内の複数クラスで使う | internal |
| 外部のプロジェクトや利用者にも公開する | public |
迷ったときは、まずprivateにするのが基本です。必要になったときだけ、internalやpublicに広げる方が安全です。
アクセス範囲を最初から広くしすぎると、あとから修正したいときに影響範囲が大きくなります。逆に、最初は狭くしておけば、クラスの内部実装を変更しやすくなります。
2. C#のアクセス修飾子とは?
C#のアクセス修飾子とは、クラス、メソッド、フィールド、プロパティなどに対して「どこからアクセスできるか」を指定するためのキーワードです。
代表的なアクセス修飾子には、public、private、protected、internal、protected internal、private protectedがあります。C#では、これらのアクセス修飾子によって型やメンバーの公開範囲を制御します。Microsoft Learn+1
2-1. アクセス修飾子の役割
アクセス修飾子の役割は、プログラムの部品に対して「使ってよい範囲」を決めることです。
たとえば、銀行口座を表すクラスがあるとします。
C#class BankAccount
{
private int balance;
public void Deposit(int amount)
{
if (amount <= 0)
{
return;
}
balance += amount;
}
}
この例では、balanceをprivateにしています。もしbalanceが外部から自由に変更できると、次のような危険な操作ができてしまいます。
C#// balanceがpublicだった場合の危険な例
// account.balance = -100000;
残高を直接書き換えられると、クラスの安全性が崩れます。そのため、外部から直接触らせたくないデータはprivateにし、必要な操作だけpublicメソッドとして公開します。
2-2. public・private・protected・internalの基本
C#でよく使う基本的なアクセス修飾子は次の4つです。
| 修飾子 | 意味 |
|---|---|
| public | どこからでもアクセスできる |
| private | 同じ型の内部からのみアクセスできる |
| protected | 同じクラスまたは派生クラスからアクセスできる |
| internal | 同じアセンブリ内からアクセスできる |
公式ドキュメントでも、publicはアクセス制限なし、protectedは包含クラスまたは派生型に制限、internalは現在のアセンブリに制限、privateは包含型に制限と説明されています。Microsoft Learn
初心者が最初に覚えるべきなのは、publicとprivateです。そのあと、プロジェクト内だけで共有したい場面でinternalを覚えると理解しやすくなります。
2-3. なぜアクセス範囲を制限する必要があるのか
アクセス範囲を制限する理由は、主に次の3つです。
| 理由 | 説明 |
|---|---|
| 変更に強くするため | 外部から使われる範囲を減らすと、内部実装を変更しやすい |
| バグを防ぐため | 不正な値の代入や想定外の呼び出しを防げる |
| 使いやすくするため | 外部に見せる機能を絞ることで、クラスの使い方がわかりやすくなる |
すべてのメンバーをpublicにすると、どこからでも呼び出せて便利に見えます。しかし、実際には「どこから使われているかわからない」「消してよいかわからない」「値が勝手に変更される」といった問題が起きやすくなります。
アクセス修飾子は、プログラムを安全に保つための境界線です。
2-4. カプセル化との関係
アクセス修飾子は、オブジェクト指向の重要な考え方である「カプセル化」と深く関係しています。
カプセル化とは、クラスの内部状態や内部処理を隠し、外部には必要な操作だけを公開する設計方法です。
C#class User
{
private string password;
public void ChangePassword(string newPassword)
{
if (newPassword.Length < 8)
{
throw new ArgumentException("パスワードは8文字以上にしてください");
}
password = newPassword;
}
}
この例では、passwordを直接変更できないようにprivateにしています。その代わり、ChangePasswordメソッドを通して変更させています。
これにより、パスワードの文字数チェックなどのルールをクラス内に閉じ込めることができます。これがカプセル化の基本です。
3. privateとは?使い方とコード例
privateは、C#で最もよく使うアクセス修飾子の1つです。クラス内部のデータや処理を外部から隠したいときに使います。
特にフィールドは、基本的にprivateにすることが多いです。外部から値を読み書きさせたい場合は、プロパティやメソッドを用意します。
3-1. privateの基本的な意味
privateは、宣言したメンバーを同じ型の中からだけ使えるようにする指定です。クラスの外側からはアクセスできません。
C#class Sample
{
private int number = 10;
private void ShowNumber()
{
Console.WriteLine(number);
}
}
このnumberとShowNumberは、Sampleクラスの中でしか使えません。
C#class Program
{
static void Main()
{
var sample = new Sample();
// sample.number; // エラー
// sample.ShowNumber(); // エラー
}
}
同じプロジェクト内にある別クラスからでも、privateメンバーにはアクセスできません。
3-2. privateフィールド・privateメソッドの例
次の例では、priceフィールドとCalculateTaxメソッドをprivateにしています。
C#class Product
{
private int price;
public Product(int price)
{
this.price = price;
}
public int GetPriceWithTax()
{
return price + CalculateTax();
}
private int CalculateTax()
{
return (int)(price * 0.1);
}
}
外部から使えるのはGetPriceWithTaxだけです。
C#var product = new Product(1000);
Console.WriteLine(product.GetPriceWithTax()); // 1100
// product.price; // エラー
// product.CalculateTax(); // エラー
この設計にすると、税込価格の計算方法をあとから変更しても、外部のコードに影響を与えにくくなります。
3-3. クラス外からprivateメンバーにアクセスできない理由
クラス外からprivateメンバーにアクセスできないのは、そのメンバーがクラス内部の実装詳細だからです。
たとえば、次のようなクラスがあるとします。
C#class Order
{
private bool isConfirmed;
public void Confirm()
{
isConfirmed = true;
}
}
isConfirmedを外部から直接変更できると、確認処理を通さずに注文状態を書き換えられてしまいます。
C#// order.isConfirmed = true; // 直接変更できると危険
そこでprivateにして、外部からはConfirmメソッドを通して操作させます。
このように、privateはクラスのルールを守るために使います。
3-4. privateを使うべき場面
privateを使うべき場面は、次のようなケースです。
| 場面 | 例 |
|---|---|
| 外部から直接変更されたくないデータ | フィールド、状態管理用の変数 |
| クラス内部だけで使う処理 | 計算用メソッド、変換用メソッド |
| 公開APIにしたくない処理 | 補助メソッド、内部ロジック |
| クラスの実装詳細 | キャッシュ、フラグ、内部的な判定処理 |
基本的には、「このメンバーを外部から呼び出す必要があるか?」と考え、必要がなければprivateにします。
C#class EmailSender
{
public void Send(string to, string body)
{
var formattedBody = FormatBody(body);
Console.WriteLine($"{to} に送信: {formattedBody}");
}
private string FormatBody(string body)
{
return $"---\n{body}\n---";
}
}
FormatBodyはメール送信処理の内部だけで使う補助メソッドなので、privateが適しています。
3-5. privateでよくあるエラーと対処法
privateでよくあるエラーは、クラス外からprivateメンバーにアクセスしようとして発生するものです。
C#class User
{
private string name = "Taro";
}
class Program
{
static void Main()
{
var user = new User();
Console.WriteLine(user.name); // エラー
}
}
この場合、nameはUserクラスの外からアクセスできません。
対処法としては、外部から読み取りたい場合はpublicプロパティを用意します。
C#class User
{
private string name = "Taro";
public string Name
{
get { return name; }
}
}
または、自動実装プロパティを使って次のように書くこともできます。
C#class User
{
public string Name { get; private set; } = "Taro";
}
これは、外部からは読み取りだけ可能で、値の変更はクラス内だけに制限する書き方です。
4. internalとは?使い方とコード例
internalは、同じアセンブリ内からアクセスできるアクセス修飾子です。外部のプロジェクトには公開したくないけれど、同じプロジェクト内の複数クラスで共有したいクラスやメソッドに使います。
privateよりもアクセス範囲は広く、publicよりは狭い、という位置づけです。
4-1. internalの基本的な意味
internalを付けた型やメンバーは、同じアセンブリ内のコードからアクセスできます。Microsoftの公式ドキュメントでも、internalアクセスは現在のアセンブリに制限されると説明されています。Microsoft Learn+1
C#internal class ReportGenerator
{
internal void Generate()
{
Console.WriteLine("レポートを生成しました");
}
}
同じプロジェクト内の別クラスからは、次のように使えます。
C#class Program
{
static void Main()
{
var generator = new ReportGenerator();
generator.Generate();
}
}
ただし、このReportGeneratorを別のプロジェクトから参照しようとしても、基本的にはアクセスできません。
4-2. アセンブリとは何かを初心者向けに解説
アセンブリとは、C#のコードをビルドした結果として作られる実行単位のことです。たとえば、.dllや.exeとして出力されるものがアセンブリです。
初心者向けには、まず次のように理解すると十分です。
| 用語 | 初心者向けのイメージ |
|---|---|
| プロジェクト | ソースコードをまとめた開発単位 |
| アセンブリ | プロジェクトをビルドして作られる成果物 |
| internal | その成果物の中だけで使える指定 |
多くの一般的な.NET開発では、1つのプロジェクトをビルドすると1つのアセンブリが作られます。そのため、最初は「internalは同じプロジェクト内で使える」と考えると理解しやすいです。
ただし、厳密にはinternalの基準は「同じ名前空間」ではなく「同じアセンブリ」です。この点は初心者がよく間違えるポイントです。
4-3. internalクラス・internalメソッドの例
internalは、クラスにもメソッドにも付けられます。
C#internal class CsvExporter
{
internal void Export()
{
Console.WriteLine("CSVを出力しました");
}
}
このクラスは、同じアセンブリ内では使えます。
C#class ExportService
{
public void Run()
{
var exporter = new CsvExporter();
exporter.Export();
}
}
また、クラスはpublicにして、メソッドだけinternalにすることもできます。
C#public class FileService
{
internal void DeleteTemporaryFiles()
{
Console.WriteLine("一時ファイルを削除しました");
}
public void SaveFile()
{
Console.WriteLine("ファイルを保存しました");
}
}
この場合、SaveFileは外部から使えますが、DeleteTemporaryFilesは同じアセンブリ内からしか使えません。
4-4. 同じプロジェクト内ならアクセスできる理由
同じプロジェクト内でinternalにアクセスできるのは、多くの場合、同じプロジェクト内のコードが同じアセンブリとしてビルドされるからです。
たとえば、次のような構成を考えます。
MyApp
├─ Program.cs
├─ Services/UserService.cs
└─ Helpers/StringHelper.cs
このMyAppプロジェクトの中にあるクラスは、ビルドされると同じアセンブリに含まれます。そのため、UserService.csにあるinternalクラスを、Program.csやStringHelper.csから利用できます。
C#internal class StringHelper
{
internal static bool IsEmpty(string value)
{
return string.IsNullOrEmpty(value);
}
}
C#class Program
{
static void Main()
{
bool result = StringHelper.IsEmpty("");
Console.WriteLine(result);
}
}
同じ名前空間である必要はありません。重要なのは、同じアセンブリに含まれているかどうかです。
4-5. internalを使うべき場面
internalを使うべき場面は、次のようなケースです。
| 場面 | 例 |
|---|---|
| プロジェクト内だけで使うクラス | 内部用サービス、ヘルパークラス |
| ライブラリ利用者には見せたくない処理 | 内部実装用のクラス |
| 複数クラスで共有したいがpublicにはしたくない処理 | 変換処理、検証処理、共通ロジック |
| 外部APIとして固定したくないメンバー | 将来変更する可能性が高いメソッド |
たとえば、ライブラリを作っている場合、利用者に見せたいクラスだけpublicにし、ライブラリ内部で使う補助クラスはinternalにします。
C#public class PaymentClient
{
public void Pay()
{
var validator = new PaymentValidator();
validator.Validate();
}
}
internal class PaymentValidator
{
internal void Validate()
{
Console.WriteLine("支払い情報を検証しました");
}
}
この設計にすると、利用者はPaymentClientだけを意識すればよく、内部のPaymentValidatorを直接使う必要がありません。
5. internalとprivateの違いをコードで比較
ここからは、internalとprivateの違いをコードで具体的に比較します。
同じように「外部に公開しない」修飾子に見えても、別クラスからアクセスできるかどうかが大きく異なります。
5-1. privateは同じクラス内だけで使える
まずはprivateの例です。
C#class Calculator
{
private int Add(int a, int b)
{
return a + b;
}
public int Execute()
{
return Add(1, 2);
}
}
AddメソッドはCalculatorクラス内から呼び出せます。
C#var calculator = new Calculator();
Console.WriteLine(calculator.Execute()); // 3
しかし、クラス外から直接Addを呼ぶことはできません。
C#// calculator.Add(1, 2); // エラー
privateは、あくまで同じクラス内だけです。
5-2. internalは同じアセンブリ内で使える
次にinternalの例です。
C#internal class Calculator
{
internal int Add(int a, int b)
{
return a + b;
}
}
同じアセンブリ内の別クラスからであれば、次のように呼び出せます。
C#class Program
{
static void Main()
{
var calculator = new Calculator();
int result = calculator.Add(1, 2);
Console.WriteLine(result); // 3
}
}
privateと違い、internalは別クラスからでもアクセスできます。ただし、その別クラスが同じアセンブリ内にある必要があります。
5-3. 別クラスからアクセスした場合の違い
同じアセンブリ内に、次の2つのクラスがあるとします。
C#class User
{
private string privateName = "Private Taro";
internal string internalName = "Internal Taro";
}
class UserPrinter
{
public void Print()
{
var user = new User();
// Console.WriteLine(user.privateName); // エラー
Console.WriteLine(user.internalName); // OK
}
}
privateNameはprivateなので、Userクラスの外からアクセスできません。
一方、internalNameはinternalなので、同じアセンブリ内のUserPrinterクラスからアクセスできます。
この違いが、privateとinternalの最も重要なポイントです。
5-4. 別プロジェクトからアクセスした場合の違い
次のように2つのプロジェクトがあるとします。
MyLibrary
└─ UserService.cs
MyApp
└─ Program.cs
MyLibraryプロジェクトに次のクラスを定義します。
C#namespace MyLibrary
{
internal class InternalUserService
{
internal void Create()
{
Console.WriteLine("ユーザー作成");
}
}
public class PublicUserService
{
public void Create()
{
Console.WriteLine("ユーザー作成");
}
}
}
MyAppプロジェクトからMyLibraryを参照した場合、PublicUserServiceにはアクセスできます。
C#var service = new PublicUserService();
service.Create();
しかし、InternalUserServiceにはアクセスできません。
C#// var service = new InternalUserService(); // エラー
internalは同じアセンブリ内に限定されるため、別プロジェクトからは基本的に見えません。
5-5. コンパイルエラーの例で違いを確認する
privateメンバーに外部からアクセスすると、次のようなコンパイルエラーになります。
C#class Sample
{
private void PrivateMethod()
{
}
}
class Program
{
static void Main()
{
var sample = new Sample();
sample.PrivateMethod(); // エラー
}
}
このような場合、C#では「アクセスできない保護レベルです」という意味のエラーが発生します。
対処法は、次のどれかです。
| 対処法 | 説明 |
|---|---|
| 呼び出しをクラス内に移動する | privateのまま安全に使う |
publicメソッドを用意する | 外部に公開する操作だけを作る |
internalに変更する | 同じアセンブリ内で共有したい場合 |
| 設計を見直す | 本当に外部から呼ぶべきか確認する |
安易にpublicへ変更するのではなく、まず「なぜ外部から呼ぶ必要があるのか」を考えることが大切です。
6. internalとprivateの使い分け方
internalとprivateの使い分けは、クラス設計の基本です。
ポイントは、「誰に使わせたいか」を考えることです。
6-1. 外部に公開したくない処理はprivateにする
そのクラスの中だけで使う処理は、基本的にprivateにします。
C#class Invoice
{
public int CalculateTotal()
{
int subtotal = CalculateSubtotal();
int tax = CalculateTax(subtotal);
return subtotal + tax;
}
private int CalculateSubtotal()
{
return 1000;
}
private int CalculateTax(int subtotal)
{
return (int)(subtotal * 0.1);
}
}
CalculateSubtotalやCalculateTaxは、請求金額を計算するための内部処理です。外部から直接呼ぶ必要がなければ、privateにするのが自然です。
privateにしておくと、あとから計算方法を変更しても、外部のコードへの影響を最小限にできます。
6-2. プロジェクト内だけで共有したい処理はinternalにする
複数のクラスで使いたいけれど、外部プロジェクトには公開したくない処理はinternalにします。
C#internal static class DateFormatter
{
internal static string Format(DateTime date)
{
return date.ToString("yyyy/MM/dd");
}
}
同じプロジェクト内の複数クラスから使えます。
C#class ReportService
{
public void Print()
{
string text = DateFormatter.Format(DateTime.Now);
Console.WriteLine(text);
}
}
このような共通処理をpublicにしてしまうと、ライブラリ外部からも使えるAPIになってしまいます。外部に公開する必要がないなら、internalの方が適しています。
6-3. ライブラリ開発でinternalを使うケース
ライブラリ開発では、internalが特に重要です。
ライブラリには、利用者に使ってほしいクラスと、ライブラリ内部だけで使うクラスがあります。
C#public class PdfGenerator
{
public void Generate()
{
var writer = new PdfInternalWriter();
writer.Write();
}
}
internal class PdfInternalWriter
{
internal void Write()
{
Console.WriteLine("PDF内部データを書き込みました");
}
}
この例では、利用者に見せたいのはPdfGeneratorです。PdfInternalWriterは内部実装なので、internalにしています。
こうすることで、ライブラリ利用者に余計なクラスを見せずに済みます。また、内部実装をあとから変更しやすくなります。
6-4. クラス設計でprivateを基本にする理由
クラス設計では、まずprivateを基本に考えるのがおすすめです。
理由は、アクセス範囲が狭いほど安全だからです。
C#class Customer
{
private string name;
private string email;
public void ChangeEmail(string newEmail)
{
if (!newEmail.Contains("@"))
{
throw new ArgumentException("メールアドレスが不正です");
}
email = newEmail;
}
}
フィールドをprivateにしておけば、必ずChangeEmailの検証処理を通して値を変更できます。
もしemailがpublicだった場合、外部から不正な値を直接入れられてしまいます。
C#// customer.email = "invalid-email"; // 直接代入できると危険
クラスの状態を守るためには、フィールドや内部処理をむやみに公開しないことが大切です。
6-5. 迷ったときの判断フロー
internalとprivateで迷ったときは、次の順番で考えると判断しやすくなります。
| 質問 | 答え | 選ぶ修飾子 |
|---|---|---|
| そのクラスの中だけで使うか? | はい | private |
| 同じアセンブリ内の別クラスでも使うか? | はい | internal |
| 外部プロジェクトの利用者にも使わせるか? | はい | public |
| 派生クラスから使わせたいか? | はい | protected系 |
基本は、できるだけ狭いアクセス範囲にすることです。
最初からinternalやpublicにするのではなく、まずprivateで作り、必要になったら範囲を広げると安全です。
7. internal privateに関連するアクセス修飾子
C#には、internalとprivate以外にもアクセス修飾子があります。
なお、C#にはinternal privateという1つのアクセス修飾子はありません。組み合わせとして存在するのは、protected internalとprivate protectedです。C#では、原則として1つの型やメンバーに指定できるアクセス修飾子は1つですが、例外的にprotected internalとprivate protectedの組み合わせがあります。Microsoft Learn+1
7-1. protectedとの違い
protectedは、同じクラスまたは派生クラスからアクセスできるアクセス修飾子です。
C#class Animal
{
protected void Eat()
{
Console.WriteLine("食べます");
}
}
class Dog : Animal
{
public void Run()
{
Eat(); // 派生クラスなのでOK
}
}
privateは同じクラス内だけですが、protectedは派生クラスからもアクセスできます。
| 修飾子 | アクセスできる範囲 |
|---|---|
| private | 同じクラス内 |
| protected | 同じクラス、派生クラス |
| internal | 同じアセンブリ内 |
継承を使う設計では、protectedを使う場面があります。
7-2. protected internalとは
protected internalは、「同じアセンブリ内」または「派生クラス」からアクセスできる指定です。公式ドキュメントでも、protected internalは現在のアセンブリ、または包含クラスから派生した型にアクセスを制限すると説明されています。Microsoft Learn+1
C#public class BaseClass
{
protected internal void Show()
{
Console.WriteLine("protected internal");
}
}
protected internalは、protectedとinternalのどちらかの条件を満たせばアクセスできます。
| アクセス元 | protected internalにアクセスできるか |
|---|---|
| 同じクラス | できる |
| 同じアセンブリの別クラス | できる |
| 別アセンブリの派生クラス | できる |
| 別アセンブリの無関係なクラス | できない |
internalよりも少し特殊で、継承とアセンブリの両方を考える必要があります。
7-3. private protectedとは
private protectedは、「同じアセンブリ内にある派生クラス」からアクセスできる指定です。公式ドキュメントでも、private protectedは現在のアセンブリ内で、包含クラスまたは派生型にアクセスを制限すると説明されています。Microsoft Learn+1
C#public class BaseClass
{
private protected void Show()
{
Console.WriteLine("private protected");
}
}
public class DerivedClass : BaseClass
{
public void Run()
{
Show(); // 同じアセンブリ内の派生クラスならOK
}
}
private protectedは、protected internalよりも制限が強いです。
| 修飾子 | 意味 |
|---|---|
| protected internal | 同じアセンブリ、または派生クラス |
| private protected | 同じアセンブリ内の派生クラス |
初心者のうちは使用頻度は高くありませんが、ライブラリ設計や継承を使う場面で登場します。
7-4. publicとの違い
publicは、どこからでもアクセスできる最も公開範囲の広いアクセス修飾子です。公式ドキュメントでは、publicはアクセス制限なしと説明されています。Microsoft Learn
C#public class UserService
{
public void CreateUser()
{
Console.WriteLine("ユーザーを作成しました");
}
}
publicにすると、同じプロジェクト内だけでなく、別プロジェクトから参照された場合にもアクセスできます。
そのため、publicは慎重に使う必要があります。特にライブラリ開発では、publicにしたクラスやメソッドは利用者が使う前提になるため、あとから削除したり仕様を変えたりしにくくなります。
| 修飾子 | 公開範囲 |
|---|---|
| private | 最も狭い |
| internal | 同じアセンブリ内 |
| public | 最も広い |
7-5. アクセス修飾子の一覧表
C#の主なアクセス修飾子を一覧で整理すると、次のようになります。
| アクセス修飾子 | アクセスできる範囲 | 初心者向けのイメージ |
|---|---|---|
| public | どこからでもアクセス可能 | 全体公開 |
| private | 同じ型の内部のみ | クラス専用 |
| protected | 同じクラスまたは派生クラス | 継承先にも公開 |
| internal | 同じアセンブリ内 | 同じプロジェクト内用 |
| protected internal | 同じアセンブリ、または派生クラス | internalとprotectedの広い方 |
| private protected | 同じアセンブリ内の派生クラス | protectedをさらに限定 |
まずは、public、private、internalの3つをしっかり理解しましょう。
そのうえで、継承を使うようになったらprotected、さらに高度な設計でprotected internalやprivate protectedを覚えるとスムーズです。
8. 初心者が間違えやすいポイント
internalとprivateはシンプルに見えますが、初心者が混乱しやすいポイントがあります。
特に「名前空間」と「アセンブリ」の違い、クラス自体にprivateを付けられるかどうか、デフォルトのアクセス修飾子には注意が必要です。
8-1. internalは「同じ名前空間」ではなく「同じアセンブリ」
internalで最も多い勘違いは、「同じ名前空間ならアクセスできる」と思ってしまうことです。
しかし、internalの基準は名前空間ではありません。同じアセンブリです。公式ドキュメントでも、internalは現在のアセンブリにアクセスを制限すると説明されています。Microsoft Learn+1
C#namespace App.Services
{
internal class UserService
{
}
}
namespace App.Controllers
{
class UserController
{
public void Run()
{
var service = new App.Services.UserService(); // 同じアセンブリならOK
}
}
}
名前空間が違っても、同じアセンブリならinternalにアクセスできます。
逆に、同じ名前空間でも別アセンブリならアクセスできません。
ProjectA
└─ namespace MyApp
ProjectB
└─ namespace MyApp
このように名前空間が同じでも、アセンブリが違えばinternalにはアクセスできません。
8-2. privateはクラス単位でアクセスが制限される
privateは、基本的にそのメンバーを含んでいる型の中からだけアクセスできます。
C#class A
{
private int value = 10;
}
class B
{
public void Run()
{
var a = new A();
// Console.WriteLine(a.value); // エラー
}
}
同じファイルに書いていても、同じ名前空間にいても、別クラスであればアクセスできません。
初心者のうちは、「同じファイルなら使えるのでは?」と思うかもしれませんが、privateの基準はファイルではなくクラスです。
8-3. クラス自体にprivateを付けられるケースと付けられないケース
C#では、名前空間の直下にある通常のクラスにprivateを付けることはできません。
C#// private class User
// {
// }
名前空間直下のクラスに使える主なアクセス修飾子は、publicまたはinternalです。公式ドキュメントでも、名前空間に直接宣言されたクラスや構造体はpublicまたはinternalにでき、アクセス修飾子を省略した場合は既定でinternalになると説明されています。Microsoft Learn
一方、クラスの中に定義する入れ子クラスにはprivateを付けられます。
C#public class OuterClass
{
private class InnerClass
{
}
}
このInnerClassは、OuterClassの内部だけで使うためのクラスです。
8-4. デフォルトのアクセス修飾子に注意する
C#では、アクセス修飾子を省略した場合のデフォルトが場所によって異なります。
| 宣言する場所 | アクセス修飾子を省略した場合 |
|---|---|
| 名前空間直下のクラス | internal |
| クラス内のフィールド | private |
| クラス内のメソッド | private |
| クラス内のプロパティ | private |
クラスや構造体のメンバーは、既定でprivateになります。公式ドキュメントでも、クラスや構造体のメンバーは既定でprivateアクセスになると説明されています。Microsoft Learn
C#class Sample
{
int number; // privateと同じ
void Show()
{
Console.WriteLine(number);
}
}
このnumberとShowは、privateを明示していなくても外部からアクセスできません。
ただし、読みやすさのために、実務ではprivateを明示することも多いです。
C#class Sample
{
private int number;
private void Show()
{
Console.WriteLine(number);
}
}
8-5. テストコードからinternalにアクセスしたい場合
通常、internalは別アセンブリからアクセスできません。しかし、テストプロジェクトから内部クラスをテストしたい場面があります。
その場合は、InternalsVisibleTo属性を使うことで、特定のアセンブリに対してinternalメンバーを見えるようにできます。
C#using System.Runtime.CompilerServices;
[assembly: InternalsVisibleTo("MyLibrary.Tests")]
たとえば、MyLibraryプロジェクトに上記を設定すると、MyLibrary.Testsプロジェクトからinternalクラスやinternalメソッドにアクセスできるようになります。
ただし、何でもテストのためにinternalへ広げればよいわけではありません。基本はpublicな振る舞いを通してテストし、どうしても必要な内部処理だけinternalとしてテストするのがよい設計です。
9. internalとprivateに関するよくある質問
最後に、C#のinternalとprivateに関するよくある質問を整理します。
初心者が疑問に思いやすいポイントを確認して、アクセス修飾子の使い分けをより明確にしましょう。
9-1. internalとprivateはどちらを優先して使うべき?
基本的には、privateを優先して使います。
理由は、アクセス範囲をできるだけ狭くした方が安全だからです。
そのクラス内だけで使うフィールドやメソッドはprivateにします。別クラスからも使う必要があり、かつ外部プロジェクトには公開したくない場合にinternalを使います。
C#class UserService
{
private void Validate()
{
// このクラス内だけで使う
}
}
internal class UserValidator
{
internal void Validate()
{
// 同じアセンブリ内の複数クラスで使う
}
}
迷ったときは、まずprivateにしましょう。必要になったらinternalへ広げるのが安全です。
9-2. internalは外部プロジェクトから絶対にアクセスできない?
通常は、別アセンブリからinternalメンバーにはアクセスできません。
ただし、InternalsVisibleTo属性を使うと、指定した別アセンブリからinternalにアクセスできるようになります。
C#[assembly: InternalsVisibleTo("MyLibrary.Tests")]
そのため、「絶対にアクセスできない」というより、「通常は同じアセンブリ内に制限されるが、明示的に許可したアセンブリには公開できる」と理解すると正確です。
主な用途は、テストプロジェクトから内部実装をテストしたい場合です。
9-3. privateメソッドは単体テストできる?
privateメソッドは、通常の方法では外部のテストクラスから直接呼び出せません。
ただし、多くの場合、privateメソッドを直接テストする必要はありません。privateメソッドは内部実装なので、そのメソッドを使っているpublicメソッドを通して振る舞いをテストします。
C#class PriceCalculator
{
public int Calculate(int price)
{
return price + CalculateTax(price);
}
private int CalculateTax(int price)
{
return (int)(price * 0.1);
}
}
この場合、CalculateTaxを直接テストするのではなく、Calculateの結果をテストします。
どうしても直接テストしたいほど複雑なprivateメソッドがある場合は、別クラスに切り出してinternalにする設計も検討できます。
9-4. internalクラスはpublicクラスと何が違う?
internalクラスとpublicクラスの違いは、外部アセンブリから見えるかどうかです。
| クラス | 同じアセンブリから | 別アセンブリから |
|---|---|---|
| public class | アクセスできる | アクセスできる |
| internal class | アクセスできる | 基本的にアクセスできない |
publicクラスは、外部プロジェクトの利用者に公開するクラスです。
internalクラスは、プロジェクトやライブラリの内部だけで使うクラスです。
C#public class OrderService
{
public void Order()
{
var validator = new OrderValidator();
validator.Validate();
}
}
internal class OrderValidator
{
internal void Validate()
{
Console.WriteLine("注文内容を検証しました");
}
}
この例では、外部に見せたいのはOrderServiceだけです。OrderValidatorは内部処理なのでinternalにしています。
9-5. 初心者はどのアクセス修飾子から覚えるべき?
初心者は、まず次の順番で覚えるのがおすすめです。
| 順番 | アクセス修飾子 | 理由 |
|---|---|---|
| 1 | public | 外部から使える基本 |
| 2 | private | クラス内部に隠す基本 |
| 3 | internal | プロジェクト内だけで共有する基本 |
| 4 | protected | 継承を使うときに必要 |
| 5 | protected internal / private protected | より細かい設計で使う |
最初からすべてを完璧に覚える必要はありません。
まずは、publicは「外に公開」、privateは「クラス内だけ」、internalは「同じアセンブリ内だけ」と覚えましょう。
まとめ
C#のinternalとprivateの違いは、アクセスできる範囲です。
privateは、同じクラスや構造体の中からのみアクセスできます。クラス内部のフィールド、補助メソッド、外部に見せたくない処理に使います。
internalは、同じアセンブリ内からアクセスできます。多くの初心者向けの理解では、「同じプロジェクト内では使えるが、外部プロジェクトには公開しない」と考えるとわかりやすいです。
| 修飾子 | アクセス範囲 | 主な使い方 |
|---|---|---|
| private | 同じクラス内 | 内部データ、補助メソッド |
| internal | 同じアセンブリ内 | プロジェクト内共有、ライブラリ内部実装 |
| public | どこからでも | 外部に公開するAPI |
初心者が迷ったときは、まずprivateを基本にしましょう。そのうえで、同じプロジェクト内の複数クラスで使う必要がある場合はinternalを選びます。
アクセス修飾子は、単にエラーを避けるための文法ではありません。クラスの責任を明確にし、バグを防ぎ、変更に強いコードを書くための重要な設計要素です。C#で保守しやすいコードを書くためにも、internalとprivateの違いをしっかり理解して使い分けましょう。

