この投稿では、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
までの仕様です。