C#のinternalとは?public・privateとの違いと使いどころを初心者向けにわかりやすく解説
はじめに
C#でクラスやメソッドを書いていると、publicやprivateと並んでinternalというキーワードを見かけることがあります。
publicは「どこからでも使える」、privateは「そのクラスの中だけで使える」というイメージを持ちやすいですが、internalは少しわかりにくいアクセス修飾子です。
internalを簡単に説明すると、「同じアセンブリ内からのみアクセスできる」ようにするためのアクセス修飾子です。多くの場合、同じプロジェクト内では使えるけれど、別のプロジェクトからは基本的に使えない、という理解から始めるとわかりやすいです。
この記事では、C#のinternalについて、public・private・protectedとの違いや、実際の使いどころ、注意点、単体テストとの関係まで初心者向けにわかりやすく解説します。
1. C#のinternalとは?まずはアクセス修飾子の基本を押さえよう
C#のinternalを理解するには、まず「アクセス修飾子」が何をするものなのかを押さえておく必要があります。
アクセス修飾子とは、クラス、メソッド、プロパティ、フィールドなどに対して「どこからアクセスできるか」を決めるキーワードです。
たとえば、次のようなものがあります。
C#public
private
protected
internal
これらを適切に使い分けることで、外部に公開する必要がある処理と、内部だけで使いたい処理を分けることができます。
1-1. internalは「同じアセンブリ内からのみアクセスできる」アクセス修飾子
internalは、同じアセンブリ内からのみアクセスできるアクセス修飾子です。
C#internal class UserService
{
internal void CreateUser()
{
Console.WriteLine("ユーザーを作成します");
}
}
このUserServiceクラスは、同じアセンブリ内であれば利用できます。しかし、別のアセンブリからは基本的にアクセスできません。
初心者のうちは、「同じプロジェクト内では使えるが、別プロジェクトからは見えないことが多い」と考えると理解しやすいです。
ただし、厳密にはinternalの範囲は「プロジェクト」ではなく「アセンブリ」です。この違いは重要なので、次で詳しく見ていきます。
1-2. アセンブリとは?初心者にもわかるプロジェクト・DLL・EXEとの関係
C#におけるアセンブリとは、コンパイルされた成果物の単位です。
たとえば、Visual Studioや.NET CLIでC#プロジェクトをビルドすると、一般的に次のようなファイルが作られます。
C#MyApp.exe
MyLibrary.dll
このexeやdllがアセンブリです。
たとえば、次のような構成を考えてみます。
MyApp
├─ MyApp.exe
MyLibrary
├─ MyLibrary.dll
MyLibraryプロジェクトで作られたMyLibrary.dllの中にinternalクラスがある場合、そのクラスはMyLibrary.dllの中からは使えます。
しかし、別のアセンブリであるMyApp.exeからは、通常アクセスできません。
つまり、internalは「同じプロジェクト内」というよりも、「同じDLLやEXEの中」という単位でアクセスを制限する仕組みです。
1-3. internalを使える対象:クラス・メソッド・プロパティ・フィールドなど
internalは、さまざまな要素に指定できます。
代表的な対象は次のとおりです。
C#internal class SampleClass
{
}
public class User
{
internal string Name { get; set; }
internal int Age;
internal void Print()
{
Console.WriteLine("表示します");
}
}
internalは、クラスそのものにも使えますし、クラスの中にあるメソッド、プロパティ、フィールドなどにも使えます。
ただし、クラスの中のメンバーにinternalを付けた場合、そのメンバーにアクセスできるかどうかは、クラス自体のアクセス修飾子にも影響されます。
たとえば、internalクラスの中にpublicメソッドを書いても、そのクラス自体が外部から見えなければ、外部アセンブリからそのpublicメソッドを直接使うことはできません。
1-4. internalを使うと何が制限されるのかをコード例で確認
次のようなライブラリプロジェクトがあるとします。
C#namespace MyLibrary
{
internal class InternalUserService
{
public void Create()
{
Console.WriteLine("ユーザー作成");
}
}
public class PublicUserService
{
public void Create()
{
Console.WriteLine("ユーザー作成");
}
}
}
同じMyLibraryアセンブリ内では、InternalUserServiceを利用できます。
C#namespace MyLibrary
{
public class UserController
{
public void Execute()
{
var service = new InternalUserService();
service.Create();
}
}
}
しかし、別のプロジェクトからMyLibraryを参照した場合、InternalUserServiceは見えません。
C#using MyLibrary;
var service = new InternalUserService(); // エラーになる
一方、publicで定義されたPublicUserServiceは、別プロジェクトからでも利用できます。
C#using MyLibrary;
var service = new PublicUserService();
service.Create();
このように、internalを使うと「同じアセンブリ内だけで使えるもの」として扱えるため、外部に公開したくない実装を隠すことができます。
2. public・private・protectedとの違い
internalを正しく使うには、他のアクセス修飾子との違いを理解することが大切です。
特に、public、private、protectedとの違いはよく使うので、しっかり整理しておきましょう。
2-1. publicとの違い:どこからでも使えるか、同じアセンブリ内だけか
publicは、どこからでもアクセスできるアクセス修飾子です。
C#public class ProductService
{
public void CreateProduct()
{
Console.WriteLine("商品を作成します");
}
}
このクラスやメソッドは、同じプロジェクト内だけでなく、別プロジェクトからも利用できます。
一方、internalは同じアセンブリ内からのみアクセスできます。
C#internal class ProductHelper
{
internal void Validate()
{
Console.WriteLine("商品情報を検証します");
}
}
ProductHelperは、外部に公開する必要のない補助的なクラスとして扱えます。
つまり、publicは「外部にも公開するもの」、internalは「内部だけで使うもの」と考えるとわかりやすいです。
2-2. privateとの違い:クラス内だけか、プロジェクト内で共有できるか
privateは、同じクラスの中からのみアクセスできます。
C#public class Calculator
{
private int Add(int a, int b)
{
return a + b;
}
public void Execute()
{
int result = Add(1, 2);
Console.WriteLine(result);
}
}
このAddメソッドは、Calculatorクラスの中からは使えますが、別のクラスからは使えません。
一方、internalは同じアセンブリ内の別クラスからもアクセスできます。
C#public class Calculator
{
internal int Add(int a, int b)
{
return a + b;
}
}
public class CalculatorRunner
{
public void Run()
{
var calculator = new Calculator();
int result = calculator.Add(1, 2);
Console.WriteLine(result);
}
}
このように、privateは「そのクラスの中だけ」、internalは「同じアセンブリ内で共有可能」という違いがあります。
2-3. protectedとの違い:継承先から使えるかどうか
protectedは、そのクラス自身と、そのクラスを継承した派生クラスからアクセスできるアクセス修飾子です。
C#public class BaseService
{
protected void Log()
{
Console.WriteLine("ログを出力します");
}
}
public class UserService : BaseService
{
public void Create()
{
Log();
}
}
この例では、UserServiceがBaseServiceを継承しているため、protectedメソッドであるLogを呼び出せます。
一方、internalは継承関係ではなく、同じアセンブリかどうかでアクセスできるかが決まります。
C#public class BaseService
{
internal void Log()
{
Console.WriteLine("ログを出力します");
}
}
この場合、同じアセンブリ内であれば、継承していないクラスからでもアクセスできます。しかし、別アセンブリの派生クラスからはアクセスできません。
つまり、protectedは「継承先に公開する」、internalは「同じアセンブリ内に公開する」という違いがあります。
2-4. protected internal・private protectedとの違いも簡単に整理
C#には、protected internalやprivate protectedというアクセス修飾子もあります。
protected internalは、「同じアセンブリ内」または「派生クラス」からアクセスできます。
C#public class BaseClass
{
protected internal void Method()
{
Console.WriteLine("protected internal");
}
}
同じアセンブリ内であれば、継承していないクラスからもアクセスできます。また、別アセンブリであっても派生クラスであればアクセスできます。
一方、private protectedは、「同じアセンブリ内の派生クラス」からアクセスできます。
C#public class BaseClass
{
private protected void Method()
{
Console.WriteLine("private protected");
}
}
protected internalは範囲が広く、private protectedは範囲が狭いと考えると理解しやすいです。
初心者のうちは、まずpublic、private、protected、internalを理解し、その後で必要に応じてprotected internalやprivate protectedを覚えるとよいでしょう。
2-5. アクセス修飾子の違いを比較表で理解する
主なアクセス修飾子の違いを整理すると、次のようになります。
| アクセス修飾子 | アクセスできる範囲 | 主な使いどころ |
|---|---|---|
| public | どこからでもアクセスできる | 外部に公開するAPIやクラス |
| private | 同じクラス内のみアクセスできる | クラス内部だけで使う処理 |
| protected | 同じクラスと派生クラスからアクセスできる | 継承先に使わせたい処理 |
| internal | 同じアセンブリ内からアクセスできる | 同じプロジェクトやライブラリ内部で使う処理 |
| protected internal | 同じアセンブリ内、または派生クラスからアクセスできる | 内部利用と継承利用の両方を許可したい場合 |
| private protected | 同じアセンブリ内の派生クラスからアクセスできる | 限定的に継承先へ公開したい場合 |
internalの特徴は、「外部には公開しないが、同じアセンブリ内では共有できる」という点です。
3. internalの基本的な使い方
ここからは、internalの基本的な書き方を見ていきます。
クラス、メソッド、プロパティ、フィールドに対してどのように指定するのかを確認しましょう。
3-1. internalクラスの書き方
internalクラスは、次のように書きます。
C#internal class OrderCalculator
{
public int CalculateTotal(int price, int quantity)
{
return price * quantity;
}
}
このOrderCalculatorクラスは、同じアセンブリ内から利用できます。
C#public class OrderService
{
public void CreateOrder()
{
var calculator = new OrderCalculator();
int total = calculator.CalculateTotal(1000, 3);
Console.WriteLine(total);
}
}
OrderCalculatorが外部から直接使われる必要がない場合は、internalにしておくことで、ライブラリの利用者に余計なクラスを見せずに済みます。
3-2. internalメソッドの書き方
メソッドにもinternalを指定できます。
C#public class ReportService
{
internal void GenerateReport()
{
Console.WriteLine("レポートを生成します");
}
}
このGenerateReportメソッドは、同じアセンブリ内の他のクラスから呼び出せます。
C#public class ReportController
{
public void Execute()
{
var service = new ReportService();
service.GenerateReport();
}
}
ただし、別のアセンブリからはこのメソッドを呼び出せません。
C#var service = new ReportService();
service.GenerateReport(); // 別アセンブリではエラー
外部に公開したいメソッドはpublic、内部だけで使いたいメソッドはinternalというように使い分けます。
3-3. internalプロパティ・フィールドの書き方
プロパティやフィールドにもinternalを指定できます。
C#public class User
{
internal string Name { get; set; }
internal int Age;
}
同じアセンブリ内であれば、次のようにアクセスできます。
C#var user = new User();
user.Name = "Taro";
user.Age = 30;
ただし、フィールドをそのまま公開すると値の変更を制御しにくくなるため、通常はフィールドよりもプロパティを使うことが多いです。
たとえば、次のように読み取り専用にすることもできます。
C#public class User
{
internal string Name { get; private set; }
public User(string name)
{
Name = name;
}
}
このように、internalとprivate setを組み合わせることで、「同じアセンブリ内からは読めるが、値の変更はクラス内だけ」という設計もできます。
3-4. アクセスできる場合・できない場合をコード例で解説
internalの動きを、同じアセンブリの場合と別アセンブリの場合で比較してみましょう。
まず、ライブラリ側に次のクラスがあるとします。
C#namespace MyLibrary
{
internal class InternalMessage
{
public string Text { get; set; } = "内部メッセージ";
}
public class MessageService
{
internal void PrintInternal()
{
var message = new InternalMessage();
Console.WriteLine(message.Text);
}
public void PrintPublic()
{
Console.WriteLine("公開メッセージ");
}
}
}
同じMyLibraryアセンブリ内では、InternalMessageもPrintInternalも使えます。
C#namespace MyLibrary
{
public class MessageRunner
{
public void Run()
{
var service = new MessageService();
service.PrintInternal();
service.PrintPublic();
}
}
}
一方、別プロジェクトからMyLibraryを参照した場合、publicなMessageServiceとPrintPublicは使えます。
C#using MyLibrary;
var service = new MessageService();
service.PrintPublic();
しかし、internalなクラスやメソッドにはアクセスできません。
C#using MyLibrary;
var message = new InternalMessage(); // エラー
service.PrintInternal(); // エラー
このように、internalは外部から見せたくない実装を隠すために役立ちます。
3-5. internalを省略した場合のデフォルトアクセス修飾子に注意
C#では、アクセス修飾子を省略した場合に、要素によってデフォルトのアクセス範囲が異なります。
トップレベルのクラスにアクセス修飾子を書かない場合、デフォルトはinternalです。
C#class SampleClass
{
}
これは次のコードと同じ意味です。
C#internal class SampleClass
{
}
一方、クラスの中のメンバーにアクセス修飾子を書かない場合、デフォルトはprivateです。
C#public class SampleClass
{
void Execute()
{
Console.WriteLine("実行します");
}
}
これは次のコードと同じ意味です。
C#public class SampleClass
{
private void Execute()
{
Console.WriteLine("実行します");
}
}
つまり、クラスでは省略するとinternal、メンバーでは省略するとprivateになります。
この違いを知らないと、「なぜ別クラスからメソッドを呼び出せないのか」「なぜ別プロジェクトからクラスが見えないのか」といった混乱につながります。
4. internalを使うメリット
internalを使う最大のメリットは、外部に公開する必要のないものを隠せることです。
C#では、何でもpublicにしてしまうと、外部から自由に使えるようになります。小さなアプリでは問題にならないこともありますが、ライブラリ開発やチーム開発では、公開範囲を適切に制限することが重要です。
4-1. 外部に公開したくない処理を隠せる
internalを使うと、外部に見せる必要のない処理を隠せます。
たとえば、ユーザー登録処理の中で入力値を検証する補助クラスがあるとします。
C#internal class UserValidator
{
public bool IsValidEmail(string email)
{
return email.Contains("@");
}
}
このUserValidatorは、ライブラリ内部では必要ですが、ライブラリの利用者が直接使う必要はないかもしれません。
その場合、internalにしておくことで、外部から直接使われることを防げます。
外部に公開するクラスは、必要最小限にした方が、使う側にとってもわかりやすくなります。
4-2. ライブラリやAPIの公開範囲を整理できる
ライブラリを作る場合、どのクラスやメソッドを外部に公開するかは非常に重要です。
たとえば、次のように公開用のクラスと内部用のクラスを分けることができます。
C#public class PaymentService
{
public void Pay()
{
var validator = new PaymentValidator();
validator.Validate();
Console.WriteLine("支払い処理を実行します");
}
}
internal class PaymentValidator
{
public void Validate()
{
Console.WriteLine("支払い情報を検証します");
}
}
この場合、ライブラリの利用者はPaymentServiceだけを使えばよく、PaymentValidatorを意識する必要はありません。
公開するAPIが整理されると、ライブラリの使い方が明確になります。
4-3. クラスやメソッドの誤用を防げる
内部処理をpublicにしてしまうと、本来想定していない使い方をされる可能性があります。
たとえば、初期化の途中でしか呼び出してはいけないメソッドをpublicにすると、外部から勝手に呼び出されてしまうかもしれません。
C#internal void InitializeCache()
{
Console.WriteLine("キャッシュを初期化します");
}
このようなメソッドをinternalにしておけば、少なくとも外部アセンブリから直接呼び出されることはありません。
もちろん、同じアセンブリ内からは呼び出せるため、必要に応じてさらにprivateにするべきかも検討する必要があります。
internalは、外部からの誤用を防ぐための有効な手段です。
4-4. 保守性が上がり、変更に強い設計にしやすい
publicにしたクラスやメソッドは、外部から使われる可能性があります。そのため、一度公開すると簡単には変更しにくくなります。
たとえば、外部で使われているpublicメソッドの名前や引数を変更すると、そのメソッドを使っているコードが壊れてしまいます。
一方、internalにしておけば、影響範囲は同じアセンブリ内に限定されます。
C#internal class OrderRule
{
public bool CanOrder()
{
return true;
}
}
このような内部用クラスであれば、外部利用者への影響を気にせず、比較的変更しやすくなります。
公開範囲を狭くしておくことは、将来的な変更に強い設計につながります。
4-5. チーム開発で「使ってよい範囲」を明確にできる
チーム開発では、「このクラスは外部から使ってよいのか」「このメソッドは内部処理なのか」がわかりにくくなることがあります。
internalを使うと、同じアセンブリ内で使うことを前提にしたクラスやメソッドであることをコード上で示せます。
C#internal class CsvParser
{
public void Parse()
{
Console.WriteLine("CSVを解析します");
}
}
このように書かれていれば、チームメンバーも「これは外部公開用ではなく、内部処理用のクラスだ」と判断しやすくなります。
アクセス修飾子は、単にコンパイル上の制限をかけるだけでなく、コードの意図を伝える役割もあります。
5. internalの使いどころ
internalは便利ですが、何でもinternalにすればよいわけではありません。
ここでは、internalが特に役立つ場面を紹介します。
5-1. 同じプロジェクト内だけで使う補助クラス
internalは、同じプロジェクト内だけで使う補助クラスに向いています。
たとえば、文字列の整形や計算処理など、アプリケーション内部でしか使わないクラスです。
C#internal class TextFormatter
{
public string ToUpperText(string text)
{
return text.ToUpper();
}
}
このクラスは、外部に公開する必要がなければinternalで十分です。
特に、ライブラリ内の補助クラスをpublicにしてしまうと、利用者にとって不要なクラスまで見えてしまいます。
外から使わせる予定がないクラスは、まずinternalを検討するとよいでしょう。
5-2. 外部公開したくないユーティリティメソッド
ユーティリティメソッドもinternalの使いどころです。
C#internal static class DateTimeHelper
{
public static bool IsWeekend(DateTime date)
{
return date.DayOfWeek == DayOfWeek.Saturday
|| date.DayOfWeek == DayOfWeek.Sunday;
}
}
このようなメソッドが同じアセンブリ内でしか使われないのであれば、publicにする必要はありません。
ただし、ユーティリティクラスは便利な反面、何でも詰め込むと責務があいまいになりやすいです。
internalにしておけば外部には公開されませんが、設計として適切かどうかは別途考える必要があります。
5-3. ライブラリ開発で公開APIと内部実装を分けたい場合
ライブラリ開発では、internalが特に重要です。
ライブラリには、外部から使ってもらうための公開APIと、その内部で使う実装があります。
C#public class MailSender
{
public void Send(string to, string message)
{
var formatter = new MailFormatter();
string body = formatter.Format(message);
Console.WriteLine($"{to} に送信: {body}");
}
}
internal class MailFormatter
{
public string Format(string message)
{
return $"[本文] {message}";
}
}
この例では、利用者が使うべきなのはMailSenderです。MailFormatterは内部処理なので、internalにしています。
このように、公開APIと内部実装を分けることで、ライブラリの使い方が明確になります。
5-4. テスト対象ではあるが本番コードでは公開したくない処理
本番コードでは外部公開したくないけれど、単体テストでは直接確認したい処理がある場合にも、internalが使われることがあります。
たとえば、複雑な計算ロジックを持つ内部クラスがある場合です。
C#internal class PriceCalculator
{
public int CalculateDiscountPrice(int price, int discount)
{
return price - discount;
}
}
このクラスは外部に公開する必要はないものの、テストでは直接検証したいことがあります。
その場合、後述するInternalsVisibleTo属性を使うことで、テストプロジェクトからinternalメンバーにアクセスできるようにできます。
ただし、テストのためだけに本来privateでよいものをinternalにするのは避けるべきです。
5-5. レイヤー分けやドメイン設計で内部処理を隠したい場合
アプリケーションをレイヤー分けして設計する場合にも、internalが役立つことがあります。
たとえば、ドメイン層の中では使うが、外部の層から直接使わせたくないクラスをinternalにできます。
C#internal class OrderStatusRule
{
public bool CanCancel(Order order)
{
return order.Status == "Created";
}
}
このようなルールクラスは、ドメイン内部の実装として扱い、外部から直接使わせないようにすることで、設計の境界を守りやすくなります。
ただし、internalだけで完全にアーキテクチャを守れるわけではありません。プロジェクト構成や名前空間、依存関係の設計もあわせて考えることが大切です。
6. internalを使うときの注意点
internalは便利なアクセス修飾子ですが、使い方を間違えると設計がわかりにくくなることもあります。
ここでは、internalを使うときに注意したいポイントを解説します。
6-1. 別プロジェクトからは基本的にアクセスできない
internalは同じアセンブリ内からのみアクセスできるため、別プロジェクトからは基本的にアクセスできません。
たとえば、次のような構成を考えます。
MyApp
MyLibrary
MyLibrary.Tests
MyLibrary内のinternalクラスは、MyAppやMyLibrary.Testsからは通常アクセスできません。
C#internal class SampleService
{
}
別プロジェクトからこのクラスを使おうとすると、コンパイルエラーになります。
そのため、別プロジェクトから使うことを前提にしているクラスやメソッドは、publicにする必要があります。
6-2. publicにすべきものまでinternalにすると使いにくくなる
本来外部に公開すべきクラスやメソッドまでinternalにしてしまうと、利用者が使えなくなってしまいます。
たとえば、ライブラリのメイン機能であるクラスをinternalにすると、外部プロジェクトから参照できません。
C#internal class UserApiClient
{
public void GetUsers()
{
Console.WriteLine("ユーザー一覧を取得します");
}
}
このクラスをライブラリ利用者に使ってもらいたいのであれば、publicにする必要があります。
C#public class UserApiClient
{
public void GetUsers()
{
Console.WriteLine("ユーザー一覧を取得します");
}
}
internalにするかどうか迷ったときは、「このクラスやメソッドは外部の利用者に使ってもらうものか」を考えると判断しやすくなります。
6-3. privateで十分なものをinternalにしない
internalはprivateよりもアクセス範囲が広いです。
そのため、クラス内部でしか使わないメソッドまでinternalにする必要はありません。
C#public class UserService
{
internal void Validate()
{
Console.WriteLine("検証します");
}
}
このValidateメソッドがUserServiceクラスの中だけで使われるのであれば、privateで十分です。
C#public class UserService
{
private void Validate()
{
Console.WriteLine("検証します");
}
}
アクセス修飾子は、できるだけ必要最小限の範囲にするのが基本です。
「外部から使わないからinternal」ではなく、「同じアセンブリ内の他のクラスから使う必要があるからinternal」と考えるとよいでしょう。
6-4. internalを増やしすぎると設計があいまいになる
internalを使えば、同じアセンブリ内の多くのクラスからアクセスできます。
便利ではありますが、何でもinternalにすると、どのクラスがどの処理を使ってよいのかがあいまいになります。
C#internal class CommonHelper
{
public void DoSomething()
{
Console.WriteLine("何かの処理");
}
}
このような汎用的すぎるinternalクラスが増えると、コードの依存関係が複雑になりやすいです。
internalは「外部に隠す」ためには役立ちますが、「内部の設計をきれいに保つ」ためには、責務の分割やクラス設計も重要です。
6-5. 単体テストでinternalメンバーを扱う場合はInternalsVisibleToを検討する
internalメンバーは、通常テストプロジェクトから直接アクセスできません。
しかし、単体テストでどうしてもinternalクラスやメソッドを直接テストしたい場合があります。
そのようなときは、InternalsVisibleTo属性を使う方法があります。
C#using System.Runtime.CompilerServices;
[assembly: InternalsVisibleTo("MyLibrary.Tests")]
この設定を追加すると、指定したアセンブリからinternalメンバーにアクセスできるようになります。
ただし、これはあくまで必要な場合に使う方法です。テストのためにアクセス範囲を広げすぎると、設計が崩れることもあるため注意しましょう。
7. internalと単体テストの関係
internalは、単体テストと関係が深いアクセス修飾子です。
特に、ライブラリ開発では「外部には公開したくないけれど、テストでは直接検証したい」というケースがあります。
7-1. internalメンバーはテストプロジェクトから直接アクセスできない
一般的に、アプリケーション本体とテストは別プロジェクトに分けます。
MyLibrary
MyLibrary.Tests
この場合、MyLibraryとMyLibrary.Testsは別アセンブリになります。
そのため、MyLibrary内のinternalクラスやメソッドは、MyLibrary.Testsから通常アクセスできません。
C#// MyLibrary側
internal class TaxCalculator
{
public int Calculate(int price)
{
return price / 10;
}
}
テストプロジェクト側で次のように書くと、アクセスできずにエラーになります。
C#// MyLibrary.Tests側
var calculator = new TaxCalculator(); // エラー
これは、internalが同じアセンブリ内にアクセスを制限しているためです。
7-2. InternalsVisibleTo属性を使うとテストプロジェクトから参照できる
テストプロジェクトからinternalメンバーにアクセスしたい場合は、InternalsVisibleTo属性を使います。
InternalsVisibleToを設定すると、指定したアセンブリに対してinternalメンバーを公開できます。
たとえば、MyLibrary.Testsというテストプロジェクトからアクセスできるようにするには、MyLibrary側に次のような設定を追加します。
C#using System.Runtime.CompilerServices;
[assembly: InternalsVisibleTo("MyLibrary.Tests")]
これにより、MyLibrary.TestsからMyLibrary内のinternalクラスやメソッドを参照できるようになります。
C#var calculator = new TaxCalculator();
int result = calculator.Calculate(1000);
外部の利用者には公開せず、テストプロジェクトには見せたい場合に便利です。
7-3. InternalsVisibleToの基本的な書き方
InternalsVisibleToは、通常AssemblyInfo.csや任意の.csファイルに記述できます。
C#using System.Runtime.CompilerServices;
[assembly: InternalsVisibleTo("MyProject.Tests")]
指定する文字列は、アクセスを許可したいアセンブリ名です。プロジェクト名と一致することが多いですが、厳密にはアセンブリ名を指定します。
たとえば、テストプロジェクトのアセンブリ名がMyProject.Testsであれば、次のように書きます。
C#[assembly: InternalsVisibleTo("MyProject.Tests")]
これで、MyProject.Testsからinternalメンバーにアクセスできます。
なお、厳密名署名されたアセンブリでは、公開キーの指定が必要になる場合があります。初心者の一般的なプロジェクトでは、まずはアセンブリ名を指定する基本形を覚えておけば十分です。
7-4. internalをテストのためだけに乱用しない考え方
単体テストのためにinternalを使うことはありますが、テストしやすくするためだけに何でもinternalにするのは避けるべきです。
本来privateでよいメソッドを、テストから直接呼びたいという理由だけでinternalにすると、クラスの内部構造が外に漏れやすくなります。
C#public class OrderService
{
internal bool IsValidOrder()
{
return true;
}
}
このメソッドが本当に同じアセンブリ内の他クラスから必要なのか、それとも単にテストしたいだけなのかを考える必要があります。
多くの場合、privateメソッドはpublicメソッドを通して間接的にテストできます。
C#public class OrderService
{
public void CreateOrder()
{
if (!IsValidOrder())
{
throw new InvalidOperationException();
}
Console.WriteLine("注文を作成します");
}
private bool IsValidOrder()
{
return true;
}
}
この場合、IsValidOrderを直接テストするのではなく、CreateOrderの振る舞いをテストすることで十分なことがあります。
internalは便利ですが、テストのために設計を崩さないように注意しましょう。
8. internalに関するよくある疑問
最後に、C#のinternalについて初心者が疑問に思いやすいポイントを整理します。
8-1. internalとprivateはどちらを使うべき?
基本的には、アクセス範囲をできるだけ狭くするのがおすすめです。
そのため、クラスの中だけで使うならprivateを使います。
C#public class Sample
{
private void Method()
{
Console.WriteLine("クラス内だけで使います");
}
}
同じアセンブリ内の他のクラスからも使う必要があるならinternalを使います。
C#public class Sample
{
internal void Method()
{
Console.WriteLine("同じアセンブリ内で使います");
}
}
迷ったときは、まずprivateで書けないかを考えるとよいでしょう。
必要になったときだけinternalに広げる方が、安全で保守しやすい設計になります。
8-2. internalクラスの中のpublicメソッドは外部から使える?
internalクラスの中にpublicメソッドがあっても、そのクラス自体が外部から見えなければ、外部アセンブリから直接使うことはできません。
C#internal class InternalService
{
public void Execute()
{
Console.WriteLine("実行します");
}
}
この場合、Executeメソッドはpublicですが、InternalServiceクラスがinternalです。
そのため、別アセンブリから次のように使うことはできません。
C#var service = new InternalService(); // エラー
service.Execute();
アクセスできるかどうかは、メンバーだけでなく、そのメンバーを持つ型のアクセス修飾子にも影響されます。
つまり、internalクラスの中のpublicメソッドは、「そのinternalクラスにアクセスできる範囲内でpublic」という意味になります。
8-3. internalは他のプロジェクトから絶対に使えない?
通常、internalは他のプロジェクトからアクセスできません。
ただし、InternalsVisibleTo属性を使うと、指定したアセンブリからアクセスできるようになります。
C#using System.Runtime.CompilerServices;
[assembly: InternalsVisibleTo("MyProject.Tests")]
この設定をすると、MyProject.Testsからinternalメンバーにアクセスできます。
そのため、「絶対に使えない」というより、「基本的には使えないが、許可したアセンブリからは使える」と理解すると正確です。
ただし、外部に正式に公開したい機能であれば、InternalsVisibleToではなくpublicにするべきです。
8-4. internalは継承できる?
internalクラスは、同じアセンブリ内であれば継承できます。
C#internal class BaseClass
{
public void Method()
{
Console.WriteLine("BaseClassのメソッド");
}
}
internal class DerivedClass : BaseClass
{
}
このように、同じアセンブリ内であればinternalクラスを継承できます。
ただし、別アセンブリからはinternalクラス自体が見えないため、通常は継承できません。
また、internalメソッドについても、同じアセンブリ内であればアクセスできますが、別アセンブリの派生クラスからはアクセスできません。
継承先に公開したい場合は、protectedやprotected internalを検討します。
8-5. internalは初心者でも覚えるべき?
internalは、C#初心者でも早めに覚えておきたいアクセス修飾子です。
最初はpublicとprivateだけでも簡単なプログラムは書けます。しかし、クラスが増えたり、プロジェクトを分けたり、ライブラリを作ったりするようになると、internalの理解が重要になります。
特に、次のような場面ではinternalが役立ちます。
外部に公開したくないクラスを作る
同じプロジェクト内だけで使う補助クラスを作る
ライブラリの公開APIを整理する
テストプロジェクトから内部処理を確認する
初心者のうちは、internalを難しく考えすぎる必要はありません。
まずは、「同じアセンブリ内だけで使える」「外部に公開したくない処理に使う」と覚えておけば十分です。
まとめ
C#のinternalは、同じアセンブリ内からのみアクセスできるアクセス修飾子です。
publicが外部にも公開するためのアクセス修飾子であるのに対し、internalはライブラリやアプリケーションの内部だけで使いたいクラスやメソッドを隠すために使います。
privateはクラス内だけ、protectedは継承先、internalは同じアセンブリ内というように、それぞれアクセスできる範囲が異なります。
internalを使うことで、外部に公開したくない処理を隠し、ライブラリやAPIの公開範囲を整理できます。また、誤用を防ぎ、保守性の高い設計にしやすくなります。
一方で、privateで十分なものまでinternalにしたり、publicにすべきものをinternalにしたりすると、かえって使いにくい設計になることがあります。
アクセス修飾子を選ぶときは、「このクラスやメソッドはどこから使われるべきか」を考えることが大切です。
C#で開発を進めるうえで、internalは非常に重要なキーワードです。まずはpublic、privateとの違いを理解し、外部公開するものと内部だけで使うものを分ける意識を持つところから始めてみましょう。

