C#のinternalとprivateの違いとは?アクセス修飾子の使い分けを初心者向けに解説

はじめに

C#でクラスやメソッド、フィールドを定義していると、internalprivateというアクセス修飾子をよく見かけます。どちらも「外部から自由にアクセスさせないための指定」ですが、アクセスできる範囲が大きく異なります。

結論から言うと、privateは「同じクラスの中だけ」で使える最も閉じた指定です。一方、internalは「同じアセンブリ内」、初心者向けに言い換えると多くの場合「同じプロジェクト内」からアクセスできる指定です。MicrosoftのC#リファレンスでも、privateは包含している型にアクセスを制限し、internalは現在のアセンブリにアクセスを制限すると説明されています。Microsoft Learn+1

この記事では、C#のinternalprivateの違いを、初心者にもわかりやすいようにコード例付きで解説します。アクセス修飾子の基本から、実務での使い分け、よくあるエラー、protected internalprivate protectedとの違いまで順番に確認していきましょう。

1. C#のinternalとprivateの違いを最初に結論で理解しよう

C#のinternalprivateの違いは、アクセスできる範囲です。

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の違いを表で比較

比較項目internalprivate
アクセスできる範囲同じアセンブリ内同じクラス、構造体などの内部
別クラスからのアクセス同じアセンブリ内なら可能不可
別プロジェクトからのアクセス基本的に不可不可
主な用途プロジェクト内だけで共有したい処理クラス内部だけで使う処理
制限の強さ中程度強い
初心者向けのイメージ同じプロジェクト内用そのクラス専用

internalは「外部プロジェクトには公開したくないが、プロジェクト内では使いたいもの」に向いています。

privateは「そのクラスの中だけで完結させたいもの」に向いています。

1-4. 初心者がまず押さえるべき使い分けの基準

初心者は、まず次の基準で使い分けると理解しやすいです。

判断基準使うアクセス修飾子
そのクラスの中だけで使うprivate
同じプロジェクト内の複数クラスで使うinternal
外部のプロジェクトや利用者にも公開するpublic

迷ったときは、まずprivateにするのが基本です。必要になったときだけ、internalpublicに広げる方が安全です。

アクセス範囲を最初から広くしすぎると、あとから修正したいときに影響範囲が大きくなります。逆に、最初は狭くしておけば、クラスの内部実装を変更しやすくなります。

2. C#のアクセス修飾子とは?

C#のアクセス修飾子とは、クラス、メソッド、フィールド、プロパティなどに対して「どこからアクセスできるか」を指定するためのキーワードです。

代表的なアクセス修飾子には、publicprivateprotectedinternalprotected internalprivate protectedがあります。C#では、これらのアクセス修飾子によって型やメンバーの公開範囲を制御します。Microsoft Learn+1

2-1. アクセス修飾子の役割

アクセス修飾子の役割は、プログラムの部品に対して「使ってよい範囲」を決めることです。

たとえば、銀行口座を表すクラスがあるとします。

C#
class BankAccount
{
private int balance;

public void Deposit(int amount)
{
if (amount <= 0)
{
return;
}

balance += amount;
}
}

この例では、balanceprivateにしています。もし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

初心者が最初に覚えるべきなのは、publicprivateです。そのあと、プロジェクト内だけで共有したい場面で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);
}
}

このnumberShowNumberは、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); // エラー
}
}

この場合、nameUserクラスの外からアクセスできません。

対処法としては、外部から読み取りたい場合は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.csStringHelper.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の違いをコードで比較

ここからは、internalprivateの違いをコードで具体的に比較します。

同じように「外部に公開しない」修飾子に見えても、別クラスからアクセスできるかどうかが大きく異なります。

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
}
}

privateNameprivateなので、Userクラスの外からアクセスできません。

一方、internalNameinternalなので、同じアセンブリ内のUserPrinterクラスからアクセスできます。

この違いが、privateinternalの最も重要なポイントです。

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の使い分け方

internalprivateの使い分けは、クラス設計の基本です。

ポイントは、「誰に使わせたいか」を考えることです。

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);
}
}

CalculateSubtotalCalculateTaxは、請求金額を計算するための内部処理です。外部から直接呼ぶ必要がなければ、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の検証処理を通して値を変更できます。

もしemailpublicだった場合、外部から不正な値を直接入れられてしまいます。

C#
// customer.email = "invalid-email"; // 直接代入できると危険

クラスの状態を守るためには、フィールドや内部処理をむやみに公開しないことが大切です。

6-5. 迷ったときの判断フロー

internalprivateで迷ったときは、次の順番で考えると判断しやすくなります。

質問答え選ぶ修飾子
そのクラスの中だけで使うか?はいprivate
同じアセンブリ内の別クラスでも使うか?はいinternal
外部プロジェクトの利用者にも使わせるか?はいpublic
派生クラスから使わせたいか?はいprotected系

基本は、できるだけ狭いアクセス範囲にすることです。

最初からinternalpublicにするのではなく、まずprivateで作り、必要になったら範囲を広げると安全です。

7. internal privateに関連するアクセス修飾子

C#には、internalprivate以外にもアクセス修飾子があります。

なお、C#にはinternal privateという1つのアクセス修飾子はありません。組み合わせとして存在するのは、protected internalprivate protectedです。C#では、原則として1つの型やメンバーに指定できるアクセス修飾子は1つですが、例外的にprotected internalprivate 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は、protectedinternalのどちらかの条件を満たせばアクセスできます。

アクセス元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をさらに限定

まずは、publicprivateinternalの3つをしっかり理解しましょう。

そのうえで、継承を使うようになったらprotected、さらに高度な設計でprotected internalprivate protectedを覚えるとスムーズです。

8. 初心者が間違えやすいポイント

internalprivateはシンプルに見えますが、初心者が混乱しやすいポイントがあります。

特に「名前空間」と「アセンブリ」の違い、クラス自体に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);
}
}

このnumberShowは、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#のinternalprivateに関するよくある質問を整理します。

初心者が疑問に思いやすいポイントを確認して、アクセス修飾子の使い分けをより明確にしましょう。

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. 初心者はどのアクセス修飾子から覚えるべき?

初心者は、まず次の順番で覚えるのがおすすめです。

順番アクセス修飾子理由
1public外部から使える基本
2privateクラス内部に隠す基本
3internalプロジェクト内だけで共有する基本
4protected継承を使うときに必要
5protected internal / private protectedより細かい設計で使う

最初からすべてを完璧に覚える必要はありません。

まずは、publicは「外に公開」、privateは「クラス内だけ」、internalは「同じアセンブリ内だけ」と覚えましょう。

まとめ

C#のinternalprivateの違いは、アクセスできる範囲です。

privateは、同じクラスや構造体の中からのみアクセスできます。クラス内部のフィールド、補助メソッド、外部に見せたくない処理に使います。

internalは、同じアセンブリ内からアクセスできます。多くの初心者向けの理解では、「同じプロジェクト内では使えるが、外部プロジェクトには公開しない」と考えるとわかりやすいです。

修飾子アクセス範囲主な使い方
private同じクラス内内部データ、補助メソッド
internal同じアセンブリ内プロジェクト内共有、ライブラリ内部実装
publicどこからでも外部に公開するAPI

初心者が迷ったときは、まずprivateを基本にしましょう。そのうえで、同じプロジェクト内の複数クラスで使う必要がある場合はinternalを選びます。

アクセス修飾子は、単にエラーを避けるための文法ではありません。クラスの責任を明確にし、バグを防ぎ、変更に強いコードを書くための重要な設計要素です。C#で保守しやすいコードを書くためにも、internalprivateの違いをしっかり理解して使い分けましょう。