Help us understand the problem. What is going on with this article?

【Unity】 Assembly Definition Files を使った上でinternalにアクセスする方法

More than 1 year has passed since last update.

追記

さらにヤバいやり方があるらしいです

今回の話の内容を2行で

Assembly Definition Filesを使ってdllを分けると、テストのときにiternalなアクセスレベルの要素にアクセスができずに困る。
対策として、AssemblyInfo.csを書こうという話。

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を組み合わせる

adfinternalを組み合わせることで、ライブラリが提供するオブジェクトの利用方法を制限することができます。
(ライブラリ内では制約なしで自由にメソッドコールできるが、ユーザからは一部のメソッドしか利用できないなど)

使用例:コンストラクタを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として分離します。
image.png

こうすることで、HogeClientのコンストラクタは外部からアクセス不可になり、必ずファクトリ経由でのインスタンス化をユーザに強制することができるようになります。

image.png

問題点:テストの時どうするの?

adfinternalを組み合わせることで、ライブラリの使用方法を制限することができるようになりました。
が、ライブラリのテストを書きたいときはこの制限は非常に邪魔になります。

例:さっきのクライアントのテストを書きたい

テスト用のadfが切られていたとして。

image.png

この状態でテストを書くと、internalにやはりアクセスできないのでHogeClientが初期化できない。

image.png

解決策:AssemblyInfo.csを配置する

解決策として、adfファイルが置かれている階層にAssemblyInfo.csを配置して次のコードを書くことで対処できます。

AssemblyInfo.cs
#if UNITY_EDITOR
using System.Runtime.CompilerServices;

// MyLibrary.Testプロジェクトに対してはinternalアクセスを許可する
[assembly: InternalsVisibleTo("MyLibrary.Test")] //adfで定義した名前と一致させる
#endif

image.png

AssemblyInfo.cs内にInternalsVisibleToを書くことで、特定のプロジェクト(dll)に対してのみinternalアクセスを許可することができます。
これでテスト用ライブラリに対して許可をしてあげることで、テスト時にinternalアクセスが可能になります。

image.png

toRisouP
virtualcast
VRシステム(バーチャルキャスト)の開発、運営、企画
https://virtualcast.jp/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away