0

0

.NET的AssemblyUnloadEventArgs类如何获取卸载信息?

小老鼠

小老鼠

发布时间:2025-08-29 08:11:01

|

786人浏览过

|

来源于php中文网

原创

AssemblyUnloadEventArgs本身不包含卸载的程序集信息,仅作为AppDomain卸载的通知信号,其设计目的是提供一个清理资源的时机而非传递数据;具体被卸载的程序集需通过自定义管理器在加载时记录,并在事件触发时通过sender参数获取对应AppDomain的上下文来查询。

.net的assemblyunloadeventargs类如何获取卸载信息?

坦白说,如果你期望从

AssemblyUnloadEventArgs
类中直接获取到具体的卸载信息,比如哪些程序集被卸载了,那你可能会有些失望。因为它是一个空的事件参数类,并没有携带任何额外的数据。它的存在,更多是作为一个信号,通知你一个
AppDomain
即将被卸载,而不是提供具体的卸载内容列表。

这个类存在的意义,更多是作为一个信号(signal),而非一个数据载体(data carrier)。当

AppDomain
即将被卸载时,它会触发
AppDomain.AssemblyUnload
事件,而
AssemblyUnloadEventArgs
就是这个事件的参数,告诉你“嘿,有事情要发生了,做好准备!”。它本身不包含卸载的程序集列表,因为这个事件的触发点是整个
AppDomain
的生命周期结束,而不是单个程序集的卸载。换句话说,当这个事件被触发时,整个应用域都在走向终结,所有加载到其中的程序集都将随之而去。你真正能从事件处理函数中获取的“信息”,其实是
sender
对象,它代表了即将被卸载的那个
AppDomain
实例。

为什么AssemblyUnloadEventArgs是空的,它有什么用?

在我看来,

AssemblyUnloadEventArgs
的设计哲学,更偏向于一种通知机制,而非详细的事件报告。它就像一个响铃,告诉你“散场了”,但不会告诉你谁走了,或者带走了什么行李。这种设计其实是有其道理的:当一个
AppDomain
被卸载时,其内部的所有程序集都会被卸载。所以,事件本身不需要再额外携带一个程序集列表来重复这个事实。

它的主要用途是提供一个钩子(hook),让开发者有机会在

AppDomain
完全卸载之前执行一些清理工作。比如,释放非托管资源、关闭文件句柄、保存状态、或者记录日志。这是一个关键的“最后机会”,在托管代码环境被彻底销毁前,完成一些必要的收尾。如果它需要携带大量数据,反而可能增加开销,甚至引发一些复杂性,毕竟在
AppDomain
即将死亡的边缘,系统资源和状态都可能变得不稳定。

如何正确监听AppDomain的卸载事件并获取相关上下文?

要监听

AppDomain
的卸载事件,我们通常会订阅
AppDomain.CurrentDomain.AssemblyUnload
事件。虽然事件参数
AssemblyUnloadEventArgs
是空的,但事件处理函数的
sender
参数却非常有用。它会传入即将被卸载的
AppDomain
实例本身。

using System;
using System.Reflection;

public class AppDomainMonitor
{
    public static void Main(string[] args)
    {
        // 创建一个新的AppDomain
        AppDomain newDomain = AppDomain.CreateDomain("MyTestDomain");

        // 在新域中加载一个程序集(例如,加载当前执行的程序集)
        // 实际应用中,你可能会通过Assembly.LoadFrom加载一个独立的DLL
        string assemblyPath = Assembly.GetExecutingAssembly().Location;
        newDomain.Load(AssemblyName.GetAssemblyName(assemblyPath));

        // 订阅新域的AssemblyUnload事件
        newDomain.AssemblyUnload += NewDomain_AssemblyUnload;

        Console.WriteLine($"AppDomain '{newDomain.FriendlyName}' created and event subscribed.");

        // 执行一些操作...
        Console.WriteLine("Performing some operations in the new domain...");

        // 卸载AppDomain
        Console.WriteLine($"Unloading AppDomain '{newDomain.FriendlyName}'...");
        AppDomain.Unload(newDomain);

        Console.WriteLine("AppDomain unload initiated. Press any key to exit.");
        Console.ReadKey();
    }

    private static void NewDomain_AssemblyUnload(object sender, AssemblyUnloadEventArgs e)
    {
        // 这里的 sender 就是即将被卸载的 AppDomain 实例
        AppDomain unloadedDomain = sender as AppDomain;
        if (unloadedDomain != null)
        {
            Console.WriteLine($"[Event Handler] AppDomain '{unloadedDomain.FriendlyName}' is about to unload.");
            // 在这里执行清理逻辑
            Console.WriteLine("[Event Handler] Performing cleanup operations...");

            // 此时尝试访问 unloadedDomain.GetAssemblies() 可能会失败或返回不完整的结果
            // 因为 AppDomain 已经处于卸载过程中
            // Console.WriteLine($"[Event Handler] Assemblies in '{unloadedDomain.FriendlyName}':");
            // foreach (var assembly in unloadedDomain.GetAssemblies())
            // {
            //     Console.WriteLine($"  - {assembly.FullName}");
            // }
        }
    }
}

通过

sender
,你可以识别出是哪个
AppDomain
正在被卸载。这对于多
AppDomain
场景下的日志记录、资源管理或特定于
AppDomain
的清理操作至关重要。比如,如果你为每个插件创建了一个独立的
AppDomain
,那么在卸载事件中,你可以根据
sender
来确定是哪个插件的
AppDomain
要被销毁,进而执行该插件特有的清理逻辑。

九歌
九歌

九歌--人工智能诗歌写作系统

下载

如果我需要知道具体哪些程序集被卸载了,应该怎么做?

这是一个非常实际的需求,但也是

AssemblyUnloadEventArgs
本身无法满足的。它不会告诉你具体哪些程序集,因为它的作用域是整个
AppDomain
的卸载。如果你真的需要这个信息,那么你需要在
AppDomain
的生命周期内,自己维护一个已加载程序集的列表。

这通常意味着,在你加载程序集的时候,就应该把它们记录下来。比如,在一个自定义的

AppDomain
管理器中,维护一个
List
或者
List
(记录程序集名称或路径)。当
AssemblyUnload
事件触发时,你可以利用
sender
(即即将被卸载的
AppDomain
实例)来索引到你之前为该
AppDomain
维护的程序集列表。

实现思路:

  1. 创建自定义
    AppDomain
    管理器:
    封装
    AppDomain
    的创建、加载程序集和卸载逻辑。
  2. 追踪加载的程序集: 在每次通过
    AppDomain.Load()
    AppDomain.ExecuteAssembly()
    加载程序集时,将程序集的完整名称或路径记录到一个与该
    AppDomain
    实例关联的集合中。
  3. 在卸载事件中查询:
    AppDomain.AssemblyUnload
    事件触发时,通过
    sender
    获取到对应的
    AppDomain
    ,然后从你的管理器中查询该
    AppDomain
    之前记录的程序集列表。
using System;
using System.Collections.Generic;
using System.Reflection;

public class CustomAppDomainManager
{
    private static Dictionary> _loadedAssembliesMap = new Dictionary>();

    public AppDomain CreateAndMonitorDomain(string domainName)
    {
        AppDomain newDomain = AppDomain.CreateDomain(domainName);
        newDomain.AssemblyLoad += NewDomain_AssemblyLoad; // 监听加载事件
        newDomain.AssemblyUnload += NewDomain_AssemblyUnload; // 监听卸载事件
        _loadedAssembliesMap[newDomain] = new List(); // 初始化列表
        Console.WriteLine($"Custom AppDomain '{domainName}' created.");
        return newDomain;
    }

    public void LoadAssemblyIntoDomain(AppDomain domain, string assemblyPath)
    {
        // 假设这里是实际的加载逻辑,例如通过反射在远程域中执行
        // 为了简化示例,我们只是模拟加载并记录
        if (System.IO.File.Exists(assemblyPath))
        {
            // 在实际的跨域加载中,你需要使用 domain.Load() 或 domain.ExecuteAssembly()
            // 这里我们只是记录路径
            _loadedAssembliesMap[domain].Add(assemblyPath);
            Console.WriteLine($"  - Assembly '{assemblyPath}' simulated loaded into '{domain.FriendlyName}'.");
        }
        else
        {
            Console.WriteLine($"  - Assembly path '{assemblyPath}' not found.");
        }
    }

    public void UnloadDomain(AppDomain domain)
    {
        Console.WriteLine($"Initiating unload for AppDomain '{domain.FriendlyName}'...");
        AppDomain.Unload(domain);
    }

    private static void NewDomain_AssemblyLoad(object sender, AssemblyLoadEventArgs args)
    {
        // 理论上,这里也可以记录,但通常我们更关心我们主动加载的
        // 如果需要,可以在这里将 args.LoadedAssembly.FullName 加入 _loadedAssembliesMap[sender as AppDomain]
        // 但通常我们只追踪我们主动加载的,避免系统程序集
        // Console.WriteLine($"[Load Event] Assembly '{args.LoadedAssembly.FullName}' loaded into '{((AppDomain)sender).FriendlyName}'.");
    }

    private static void NewDomain_AssemblyUnload(object sender, AssemblyUnloadEventArgs e)
    {
        AppDomain unloadedDomain = sender as AppDomain;
        if (unloadedDomain != null)
        {
            Console.WriteLine($"\n[Unload Event] AppDomain '{unloadedDomain.FriendlyName}' is about to unload.");
            if (_loadedAssembliesMap.TryGetValue(unloadedDomain, out List assemblies))
            {
                Console.WriteLine($"  Previously tracked assemblies in '{unloadedDomain.FriendlyName}':");
                foreach (var assemblyPath in assemblies)
                {
                    Console.WriteLine($"    - {assemblyPath}");
                }
                _loadedAssembliesMap.Remove(unloadedDomain); // 清理追踪数据
            }
            else
            {
                Console.WriteLine($"  No tracked assemblies found for '{unloadedDomain.FriendlyName}'.");
            }
            Console.WriteLine($"[Unload Event] Cleanup complete for '{unloadedDomain.FriendlyName}'.");
        }
    }

    public static void Main(string[] args)
    {
        CustomAppDomainManager manager = new CustomAppDomainManager();

        AppDomain domain1 = manager.CreateAndMonitorDomain("PluginDomain1");
        manager.LoadAssemblyIntoDomain(domain1, "C:\\Plugins\\MyPluginA.dll");
        manager.LoadAssemblyIntoDomain(domain1, "C:\\Plugins\\MyPluginB.dll");

        AppDomain domain2 = manager.CreateAndMonitorDomain("PluginDomain2");
        manager.LoadAssemblyIntoDomain(domain2, "C:\\Plugins\\MyPluginC.dll");

        Console.WriteLine("\n--- Simulating some work ---\n");
        System.Threading.Thread.Sleep(1000); // 模拟工作

        manager.UnloadDomain(domain1);
        System.Threading.Thread.Sleep(500); // 稍微等待

        manager.UnloadDomain(domain2);

        Console.WriteLine("\nAll domains unloaded. Press any key to exit.");
        Console.ReadKey();
    }
}

通过这种方式,你可以在

AppDomain
卸载时,获取到你之前加载到该
AppDomain
中的具体程序集信息。这虽然不是直接从
AssemblyUnloadEventArgs
中获取,但却是解决“需要知道具体哪些程序集被卸载”这一问题的有效且常用的策略。它要求你对
AppDomain
的生命周期管理有更主动的控制。

相关专题

更多
string转int
string转int

在编程中,我们经常会遇到需要将字符串(str)转换为整数(int)的情况。这可能是因为我们需要对字符串进行数值计算,或者需要将用户输入的字符串转换为整数进行处理。php中文网给大家带来了相关的教程以及文章,欢迎大家前来学习阅读。

318

2023.08.02

高德地图升级方法汇总
高德地图升级方法汇总

本专题整合了高德地图升级相关教程,阅读专题下面的文章了解更多详细内容。

43

2026.01.16

全民K歌得高分教程大全
全民K歌得高分教程大全

本专题整合了全民K歌得高分技巧汇总,阅读专题下面的文章了解更多详细内容。

84

2026.01.16

C++ 单元测试与代码质量保障
C++ 单元测试与代码质量保障

本专题系统讲解 C++ 在单元测试与代码质量保障方面的实战方法,包括测试驱动开发理念、Google Test/Google Mock 的使用、测试用例设计、边界条件验证、持续集成中的自动化测试流程,以及常见代码质量问题的发现与修复。通过工程化示例,帮助开发者建立 可测试、可维护、高质量的 C++ 项目体系。

24

2026.01.16

java数据库连接教程大全
java数据库连接教程大全

本专题整合了java数据库连接相关教程,阅读专题下面的文章了解更多详细内容。

35

2026.01.15

Java音频处理教程汇总
Java音频处理教程汇总

本专题整合了java音频处理教程大全,阅读专题下面的文章了解更多详细内容。

16

2026.01.15

windows查看wifi密码教程大全
windows查看wifi密码教程大全

本专题整合了windows查看wifi密码教程大全,阅读专题下面的文章了解更多详细内容。

56

2026.01.15

浏览器缓存清理方法汇总
浏览器缓存清理方法汇总

本专题整合了浏览器缓存清理教程汇总,阅读专题下面的文章了解更多详细内容。

16

2026.01.15

ps图片相关教程汇总
ps图片相关教程汇总

本专题整合了ps图片设置相关教程合集,阅读专题下面的文章了解更多详细内容。

9

2026.01.15

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
10分钟--Midjourney创作自己的漫画
10分钟--Midjourney创作自己的漫画

共1课时 | 0.1万人学习

Midjourney 关键词系列整合
Midjourney 关键词系列整合

共13课时 | 0.9万人学习

AI绘画教程
AI绘画教程

共2课时 | 0.2万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号