【C#.NET解説】AppDomainの基本機能からアセンブリ管理と実践的な活用法まで完全ガイド

2024年12月30日月曜日

システム プロセス 制御フロー

t f B! P L

名前空間 System

AppDomain

自己紹介

「そなたたちよ、聞くがよい。我は隼のごとき目で全てを見通す力を持つ者なり。AppDomain」とは、.NETで提供されるアプリケーションドメインを管理するクラスである。これを使えば、アプリケーションの実行環境を分離し、信頼性とセキュリティを向上させることが可能だ。アプリケーションの起動パスを取得したり、アセンブリを動的にロード・アンロードする際に活躍する。

基本機能

まずはAppDomainの基本機能を紹介しよう。以下のコードは、現在のアプリケーションの起動パスを取得する方法を示しておる。

C#

using System;

class Program
{
    static void Main()
    {
        // AppDomainで起動パスを取得
        string startupPath = AppDomain.CurrentDomain.BaseDirectory;
        Console.WriteLine("起動パス: " + startupPath);
    }
}

Application.StartupPathとの違いだが、前者はWPFやWindows Formsで使用され、AppDomainはLinuxなどでも使用可能で範囲が広いのだ。

よく使う場面と注意点

AppDomainの最大の強みは、アセンブリを動的にロードし、アンロードできる点にある。例えば、MyAssembly.dllをロードし、関数を呼び出す例を示そう。

C# MyAssembly.dll側

// MyClass.cs
namespace MyNamespace
{
    public class MyClass
    {
        public void MyMethod()
        {
            Console.WriteLine("MyMethod called in MyAssembly.dll");
        }
    }
}

C# 呼び出し側

using System;
using System.Reflection;

class Program
{
    static void Main()
    {
        // 現在のアプリケーションドメインを取得
        AppDomain currentDomain = AppDomain.CurrentDomain;
        Console.WriteLine("Current AppDomain: " + currentDomain.FriendlyName);

        // 新しいアプリケーションドメインを作成
        AppDomain newDomain = AppDomain.CreateDomain("NewAppDomain");
        Console.WriteLine("New AppDomain: " + newDomain.FriendlyName);

        try
        {
            // 新しいAppDomainでアセンブリをロード
            newDomain.Load("MyAssembly");

            // 新しいAppDomainでメソッドを呼び出すためのデリゲートを作成
            newDomain.DoCallBack(CallMethodInNewDomain);
        }
        finally
        {
            // 新しいAppDomainをアンロード
            AppDomain.Unload(newDomain);
            Console.WriteLine("New AppDomain unloaded.");
        }
    }

    static void CallMethodInNewDomain()
    {
        // MyAssembly.dllの型を取得
        Type type = Type.GetType("MyNamespace.MyClass, MyAssembly");

        // パブリックメソッドを取得
        MethodInfo method = type.GetMethod("MyMethod");

        // インスタンスを作成してメソッドを呼び出し
        object obj = Activator.CreateInstance(type);
        method.Invoke(obj, null);
    }
}

引数付き関数を呼び出す実装例

次は、引数付きの関数を呼び出す場合の例だ。

C# MyAssembly.dll側

// MyClass.cs
namespace MyNamespace
{
    public class MyClass
    {
        public void MyMethod(string message)
        {
            Console.WriteLine($"MyMethod called in MyAssembly.dll with message: {message}");
        }
    }
}

C# 呼び出し側

using System;
using System.Reflection;

class Program
{
    static void Main()
    {
        // 現在のアプリケーションドメインを取得
        AppDomain currentDomain = AppDomain.CurrentDomain;
        Console.WriteLine("Current AppDomain: " + currentDomain.FriendlyName);

        // 新しいアプリケーションドメインを作成
        AppDomain newDomain = AppDomain.CreateDomain("NewAppDomain");
        Console.WriteLine("New AppDomain: " + newDomain.FriendlyName);

        try
        {
            // 新しいAppDomainでアセンブリをロード
            newDomain.Load("MyAssembly");

            // 新しいAppDomainでメソッドを呼び出すためのデリゲートを作成
            newDomain.DoCallBack(() => CallMethodInNewDomain("Hello from Main AppDomain!"));
        }
        finally
        {
            // 新しいAppDomainをアンロード
            AppDomain.Unload(newDomain);
            Console.WriteLine("New AppDomain unloaded.");
        }
    }

    static void CallMethodInNewDomain(string message)
    {
        // MyAssembly.dllの型を取得
        Type type = Type.GetType("MyNamespace.MyClass, MyAssembly");

        // パブリックメソッドを取得
        MethodInfo method = type.GetMethod("MyMethod");

        // インスタンスを作成してメソッドを呼び出し
        object obj = Activator.CreateInstance(type);
        method.Invoke(obj, new object[] { message });
    }
}

異なるバージョンのアセンブリを同時に使用する

異なるバージョンの同じアセンブリを同時に使用する際、AppDomainはその解決策を提供する。以下は、バージョンの競合を回避するための例だ。

C# MyAssembly1.dll

// MyClass1.cs
namespace MyNamespace1
{
    public class MyClass
    {
        public void MyMethod()
        {
            Console.WriteLine("MyMethod called in MyAssembly1.dll");
        }
    }
}

C# MyAssembly2.dll

// MyClass2.cs
namespace MyNamespace2
{
    public class MyClass
    {
        public void MyMethod()
        {
            Console.WriteLine("MyMethod called in MyAssembly2.dll");
        }
    }
}

フォルダ構造

/app/bin
    Program.exe
/bin1
    MyAssembly1.dll
/bin2
    MyAssembly2.dll

C#

using System;
using System.Reflection;

class Program
{
    static void Main()
    {
        string appBase = AppDomain.CurrentDomain.BaseDirectory;

        // AppDomain1を作成
        AppDomainSetup setup1 = new AppDomainSetup
        {
            ApplicationBase = appBase + @"..\bin1"
        };
        AppDomain domain1 = AppDomain.CreateDomain("Domain1", null, setup1);

        // AppDomain2を作成
        AppDomainSetup setup2 = new AppDomainSetup
        {
            ApplicationBase = appBase + @"..\bin2"
        };
        AppDomain domain2 = AppDomain.CreateDomain("Domain2", null, setup2);

        try
        {
            // AppDomainにアセンブリをロードしてメソッドを呼び出す
            domain1.DoCallBack(() => LoadAndCallMethod(appBase + @"..\bin1\MyAssembly1.dll", "MyNamespace1.MyClass", "MyMethod"));
            domain2.DoCallBack(() => LoadAndCallMethod(appBase + @"..\bin2\MyAssembly2.dll", "MyNamespace2.MyClass", "MyMethod"));
        }
        finally
        {
            // AppDomainのアンロード
            AppDomain.Unload(domain1);
            AppDomain.Unload(domain2);
        }
    }

    static void LoadAndCallMethod(string assemblyPath, string typeName, string methodName)
    {
        Assembly assembly = Assembly.LoadFrom(assemblyPath);
        Type type = assembly.GetType(typeName);
        MethodInfo method = type.GetMethod(methodName);
        object obj = Activator.CreateInstance(type);
        method.Invoke(obj, null);
    }
}

まとめとその他のメソッド

AppDomainを使えば、安全性と柔軟性を保ちながら複雑なアセンブリ管理が可能だ。さらに、アセンブリのシャドウコピーや非同期処理など、便利な機能も備えている。

さあ、そなたたちよ、AppDomainの力を得た今、さらなる高みへと飛翔するがよい!

このブログを検索

QooQ