追記
さらにヤバいやり方があるらしいです
今回の話の内容を2行で
Assembly Definition Files
を使ってdllを分けると、テストのときにiternal
なアクセスレベルの要素にアクセスができずに困る。
対策として、AssemblyInfo.cs
を書こうという話。
#if UNITY_EDITOR
using System.Runtime.CompilerServices;
// 指定したdllから、internalアクセスレベルへのアクセスを許可する
[assembly: InternalsVisibleTo("MyLibrary.Test")] //adfで定義した名前と一致させる
#endif
本題
Assembly Definition Files
Unityには、Assembly Definition Files
(以下adf
)という機能があります。
これはadf
ファイルが置かれたディレクトリのスクリプトを別のdllに分けてコンパイルするという機能です。
adf
ファイルを使うことで、プロジェクト本体とは独立性が高い部分を別のライブラリに切り出して依存関係を管理するなど、プロジェクト構成の管理に使うことができます。
詳しくはテラシュールブログさんのこの記事を参考にしてください。
C#のinternalアクセシビリティ
話が変わって、C#にはiternal
というアクセスレベルが用意されています。
これは「同一のプロジェクト内(同一dll内)からのみアクセス可能にする」というアクセスレベルです。
Assembly Definition Filesとinternalを組み合わせる
adf
とinternal
を組み合わせることで、ライブラリが提供するオブジェクトの利用方法を制限することができます。
(ライブラリ内では制約なしで自由にメソッドコールできるが、ユーザからは一部のメソッドしか利用できないなど)
使用例:コンストラクタをinternalにする
たとえば、こんなクライアントがあったとします。
using System;
namespace MyLibrary
{
/// <summary>
/// 何かのクライアントがあって
/// </summary>
public class HogeClient
{
private string _token;
/// <summary>
/// インスタンス化にはTokenが必要なのだが、
/// このTokenを使ったコンストラクタはライブラリ外に露出させたくないので internal
/// </summary>
/// <param name="token"></param>
internal HogeClient(string token)
{
_token = token;
}
}
/// <summary>
/// Clientのファクトリー
/// </summary>
public class HogeClientFactory
{
/// <summary>
/// Clientの生成時に、Tokenを取得して埋め込む
/// 不正なTokenを埋め込んだ状態でClientは生成できない
/// </summary>
public HogeClient CreateClient()
{
var token = FetchToken();
if (!ValidationToken(token)) throw new Exception("Invalid token.");
return new HogeClient(token);
}
private string FetchToken()
{
/**
* ScriptableObjectとか、サーバとか、何らかの方法で
* Tokenを取得する想定
*/
return "sample_token";
}
private bool ValidationToken(string token)
{
/**
* Tokenの有効性を確認したりとか?
*/
return true;
}
}
}
ここに、次のようなadf
ファイルを定義し、HogeClient
を別のdllとして分離します。
こうすることで、HogeClient
のコンストラクタは外部からアクセス不可になり、必ずファクトリ経由でのインスタンス化をユーザに強制することができるようになります。
問題点:テストの時どうするの?
adf
とinternal
を組み合わせることで、ライブラリの使用方法を制限することができるようになりました。
が、ライブラリのテストを書きたいときはこの制限は非常に邪魔になります。
例:さっきのクライアントのテストを書きたい
テスト用のadf
が切られていたとして。
この状態でテストを書くと、internal
にやはりアクセスできないのでHogeClient
が初期化できない。
解決策:AssemblyInfo.csを配置する
解決策として、adf
ファイルが置かれている階層にAssemblyInfo.cs
を配置して次のコードを書くことで対処できます。
#if UNITY_EDITOR
using System.Runtime.CompilerServices;
// MyLibrary.Testプロジェクトに対してはinternalアクセスを許可する
[assembly: InternalsVisibleTo("MyLibrary.Test")] //adfで定義した名前と一致させる
#endif
AssemblyInfo.cs
内にInternalsVisibleTo
を書くことで、特定のプロジェクト(dll)に対してのみinternalアクセスを許可することができます。
これでテスト用ライブラリに対して許可をしてあげることで、テスト時にinternalアクセスが可能になります。