いろいろ備忘録日記

主に .NET とか Go とか Flutter とか Python絡みのメモを公開しています。

.NET クラスライブラリ探訪-026 (System.Runtime.CompilerServices.TypeForwardedToAttribute)(型の移動)


今回は、System.Runtime.CompilerServices.TypeForwardedToAttributeについてです。
最初に申し上げておきますが、私自身この属性を使ったことがありません。


気になったので、調べてみた程度です。
間違っているかもしれませんが、ご了承下さい。


この属性を付与すると、以下の事が出来るようになります。

別のライブラリに存在する特定の型の呼び出しを転送(Forward)することが出来る。


何を言っているのかよくわかりませんね。w
実際の例をイメージした方がわかりやすいです。


以下のような状態をイメージしてみて下さい。


現在すごく大きなアプリケーションを複数の人間(会社)で行っています。
アプリ本体、各DLLはそれぞれの担当者(会社)が別々で行っています。
こんな感じだとします。

  • App.exe (アプリ本体)
    • ClassLibrary1.dll (ClassA, ClassBが存在)


[App.cs](アプリ本体)

public class App
{
    static void Main()
    {
        ClassA a = new ClassA();
        a.Execute();

        ClassB b = new ClassB();
        b.Execute();

        Console.WriteLine("Press any key to exit...");
        Console.ReadLine();
    }
}


[Classes.cs](ClassA, ClassB)(ライブラリ側)

public class ClassA
{
    public void Execute()
    {
        Console.WriteLine("Execute on {0}", GetType().Name);
    }
}

public class ClassB
{
    public void Execute()
    {
        Console.WriteLine("Execute on {0}", GetType().Name);
    }
}


Appクラスは内部でClassAとClassBを呼んで使っています。
なので、当然アプリ本体はClassLibrary.dllを参照設定しています。


この状態で、例えば、仕様変更が発生し、ClassAとClassBを別々のDLLに
分けなければならない事になりました。でも、アプリ本体は既に出来上がっているので
別々のDLLに分けたとしても、アプリ本体を再コンパイルすることは許されないとします。(GACも駄目)
可能な事は新たに作成したDLLを配置することだけです。
(現実的な話だとクラスライブラリが 超巨大になり分けることにしたり
または、別の会社が使えるように特定の機能分のクラスを別のDLLに切り出したりする事はたまにあります。)


こんな状態(かなり稀ですが・・)の時に利用できるのが、TypeForwardedToAttributeさんです。
要は、呼び元の再コンパイルを行わず、元々参照設定しているライブラリに転送設定を行い
あたかも、そのライブラリに存在しているかのように呼び出す事が出来るようにします。


と、ここまでグダグダと書いてきましたがやり方自体はすごく簡単です。
今回は、ClassBを別のDLLに移動しなければならない状態であるとします。(ClassLibrary2.dll)


まず、ClassBを移動させます。
[Classes.cs](ClassLibrary2.dll側)

public class ClassB
{
    public void Execute()
    {
        Console.WriteLine("Execute on {0}", GetType().Name);
    }
}

次に、ClassLibrary1.dll側、つまり、ClassA側に以下の記述をいれます。

[Classes.cs] (ClassLibrary.dll側)

// 以下を記述
[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(ClassB))]

public class ClassA
{
    public void Execute()
    {
        Console.WriteLine("Execute on {0}", GetType().Name);
    }
}


記述するのは、どこでもいいです。VisualStudio上での場合はAssemblyInfo.cs
あたりが分かりやすいですね。


当然、ClassLibrary.dll側にはClassLibrary2.dllの参照設定が必要です。
(ClassBはClassLibrary2.dllに存在するため)


後は、これら2つのDLLを実行ファイルが存在するディレクトリにコピーします。
そうすると以下のような配置状態となります。

  • App.exe (アプリ本体)
    • ClassLibrary.dll
    • ClassLibrary2.dll (App.exe側からは参照設定されていない)


実行するとちゃんと動きます。
ClassBはApp.exe側から参照設定されていないClassLibrary2.dllに存在しますが
ClassLibrary1側にてClassBの呼び出しはClassLibrary2の方に存在するClassBに転送しなさいと
指定している為、参照設定されていませんがうまく動くという訳です。


勉強するにあたって以下のブログを参考にしました。
(本来MSDNを見て勉強すべきかもしれませんが、MSDN側にはほとんどこの属性についての説明がありません・・・)


================================
過去の記事については、以下のページからご参照下さい。

サンプルコードは、以下の場所で公開しています。