この投稿では、2019年におけるC#のアクセス修飾子を紹介します。
C# 7.2から新たにprivate protectedが新たに加わりました。これは、『「同一のアセンブリ」 かつ 「その型とその派生型」はアクセスできる』というアクセスレベルです。そしてゲームエンジンUnityにAssembly Definition FilesやPackage Managerが加わったことで、Unity開発者は「アセンブリ」を意識して開発する機会が増えました。継続的なライブラリ更新のために、このようなアセンブリに関係するアクセス修飾子をしっかりと理解すべき状況となっています。
自分で開発したライブラリを社内で公開している人、世の中に広く公開したい人は、2019年におけるC#のアクセス修飾子理解することをオススメします。
アクセス修飾子とアクセスレベル
アクセス修飾子は以下の4個です
- public
- protected
- internal
- private
アクセスレベルは以下の6種です。
- public : 無制限
- protected : その型とその派生型はアクセスできる
- internal : 同一のアセンブリ(同じDllやExe)からのみアクセスできる
- protected internal : 「同一のアセンブリ」 もしくは 「その型とその派生型」はアクセスできる
- private protected: 「同一のアセンブリ」 でありかつ 「その型とその派生型」はアクセスできる
- private : その型からのみアクセスできる
※ private protectedがC# 7.2から新たに加わったアクセスレベルです。
※ 中間言語のMSILとして「protected and internal」相当のものは以前から存在していましたが、C#で加わったのはC# 7.2からです。
protectedはでかい。なぜなら・・・
ライブラリ開発者にとって、アクセスレベルprotectedは非常にアクセス範囲が広いアクセスレベルです。
『あなたはライブラリ開発者で、コード形式ではなくアセンブリ(DLL形式)でライブラリを配布しており、あるAPIを削除しようとしています。』このような状況を考えてみてください。
- 
削除しようとしているAPIが privateアクセスレベルだったら、そのAPIは問題なく削除できます。ライブラリ利用者はprivateメンバにアクセスできないからです。
- 
削除しようとしているAPIが internalアクセスレベルだったら、そのAPIは問題なく削除できますね。ライブラリ利用者はinternalなメンバ・型にアクセスできないからです。
- 
削除しようとしているAPIが publicアクセスレベルだったら、そのAPIを削除した場合破壊的変更になってしまうので、簡単に削除できません。Obsoleteにし移行方法をアナウンスするなどコストをかける必要があります。(もしくは、いきなり削除してライブラリ利用者に迷惑をかけるしかありません)
では、削除しようとしているAPIがprotectedアクセスレベルだった場合はどうでしょう?ライブラリ開発において、protectedアクセスレベルは、publicアクセスレベルと同等に慎重に扱わないといけません。protectedアクセスレベルは、「その型とその派生型はアクセスできる」ですが、派生された型であればライブラリ利用者は自由に使うことができます。そのため、protected APIを削除することは破壊的な変更になってしまいます。publicアクセスレベルのAPIと同様に、Obsoleteにし移行方法をアナウンスするなどコストをかける必要があります。(もしくは、いきなり削除してライブラリ利用者に迷惑をかける覚悟を持つ必要があります。)
次にprotected internalアクセスレベルです。protected internalアクセスレベルは「同一のアセンブリ」 もしくは 「その型とその派生型」はアクセスできます。そのため、ライブラリ利用者は派生された型の内部でprotected internalなメンバにアクセスできます。そのため、protected internalなAPIを削除することは破壊的な変更になってしまいます。これもpublicアクセスレベルと同等に慎重に扱わないといけません。
では、C# 7.2から加わったprivate protectedアクセスレベルはどうでしょうか?これは、「同一のアセンブリ」 でありかつ 「その型とその派生型」はアクセスできるというアクセスレベルです。ライブラリ利用者はprotected internalなメンバにアクセスすることができません。「同一のアセンブリ」ではないからです。そのため、そのAPIは問題なく削除できます。
おさらいしましょう。
アセンブリ(DLL形式)でライブラリを配布しているのであれば、
- public
- protected
- protected internal
なアクセスレベルメンバ・型の削除は、破壊的な変更になってしまうので簡単には削除できません。Obsoleteにし移行方法をアナウンスするなどコストをかける必要があります。(もしくは、いきなり削除してライブラリ利用者に迷惑をかけるしかありません)
protectedそして、protected internalは実は結構アクセス範囲が広いということに注意してください。
protected internalは、『「同一のアセンブリ」 または 「その型とその派生型」はアクセスできる』であり、『「同一のアセンブリ」 かつ 「その型とその派生型」はアクセスできる』ではないことに注意してください。
※ これはライブラリの話であり、アプリケーションの話とは違うのでその点に注意してください。
※ ベータ版など、破壊的な変更が入る予定があるライブラリもたくさんあります。
リフレクションを使えばprivateメンバにもアクセスできる
リフレクションを使えばprivateメンバにアクセスできます。
ライブラリ利用者に対して、どうしても隠しておかないといけない情報はprivateにしたとしても、リフレクションを使えばアクセスできます。この点に注意してください。
アクセス修飾の順番
private protected は、protected privateと同じ意味です。
また、protected internalとinternal protectedと同じ意味です。
これらは修飾子の順番を変えても有効です。
interanalな型をテストしたい場合
interanalな型を別アセンブリのテストプロジェクトでテストしたい場合、InternalsVisibleToAttributeを活用しましょう。
その他いろいろ
アクセス修飾子を省略した場合や、入れ子の型のアクセス修飾子については、公式ドキュンメントをみてください。
関連
補足
この記事は、
- Unity 2019.2
- C# 8
までの仕様です。