You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C#反射疑问:加载多个DLL时第二个DLL输出与第一个一致的原因

问题描述

制作简单模组加载器时,加载单个DLL正常,但加载多个DLL时出现异常:第二个DLL的输出与第一个完全相同,尽管两个DLL的MainMod类中Start方法实现完全不同。

加载器核心代码:

private static void Main()
{
    string[] modFolders =
    [
        "Q:\\VisualStudio\\Projects\\C#\\ModLoader\\ModsFolder\\Mod1",
        "Q:\\VisualStudio\\Projects\\C#\\ModLoader\\ModsFolder\\Mod2"
    ];

    foreach (string modFolder in modFolders)
    {
        string dllFile = Directory.GetFiles(modFolder, "*.dll*").First();
        Assembly assembly = Assembly.LoadFrom(dllFile);
        Type type = assembly.GetType($"MainMod");

        if (type is not null)
        {
            object instance = Activator.CreateInstance(type);
            MethodInfo method = type.GetMethod("Start");
            object result = method.Invoke(instance, null);
            Console.WriteLine($"result: {result}");
        }
    }
}

第一个DLL代码:

public class MainMod
{
    public static int Start() => 100;
}

第二个DLL代码:

public class MainMod
{
    public static string Start() => "Text";
}

程序实际输出:

result: 100
result: 100

问题原因

两个DLL中的类都使用了无命名空间的MainMod名称,.NET应用程序域中不允许存在完全同名的类型。当加载第一个DLL后,MainMod类型已被加载到当前应用程序域,后续加载的第二个DLL中同名无命名空间的类型会被忽略,导致始终调用第一个DLL的Start方法。

另外注意:原代码中调用静态方法Start时,Activator.CreateInstance(type)是多余的,静态方法无需实例即可调用。

解决方案

1. 为模组类添加唯一命名空间(推荐)

给每个模组的类添加独立命名空间,确保类型全局唯一:
第一个DLL修改为:

namespace Mod1
{
    public class MainMod
    {
        public static int Start() => 100;
    }
}

第二个DLL修改为:

namespace Mod2
{
    public class MainMod
    {
        public static string Start() => "Text";
    }
}

同时修改加载器中获取类型的代码,指定完整的命名空间+类型名:

// 可以根据模组标识动态获取命名空间,示例中直接指定
Type type = assembly.GetType($"Mod1.MainMod"); // 对应第一个模组
// 第二个模组对应 Mod2.MainMod

2. 使用Assembly.LoadFile替代Assembly.LoadFrom

Assembly.LoadFrom会共享已加载的依赖类型,而Assembly.LoadFile会将程序集作为独立文件加载,不共享类型(但仍需注意,若存在完全同名类型仍可能有冲突,仅作为临时方案):

Assembly assembly = Assembly.LoadFile(dllFile);

3. 为每个模组创建独立应用程序域(进阶隔离方案)

通过创建独立的应用程序域加载每个模组,实现完全隔离,避免类型冲突:

private static void Main()
{
    string[] modFolders =
    [
        "Q:\\VisualStudio\\Projects\\C#\\ModLoader\\ModsFolder\\Mod1",
        "Q:\\VisualStudio\\Projects\\C#\\ModLoader\\ModsFolder\\Mod2"
    ];

    foreach (string modFolder in modFolders)
    {
        string dllFile = Directory.GetFiles(modFolder, "*.dll").First();
        AppDomainSetup setup = new AppDomainSetup { ApplicationBase = Path.GetDirectoryName(dllFile) };
        AppDomain domain = AppDomain.CreateDomain($"ModDomain_{Path.GetFileNameWithoutExtension(dllFile)}", null, setup);
        
        try
        {
            ModProxy proxy = (ModProxy)domain.CreateInstanceAndUnwrap(
                typeof(ModProxy).Assembly.FullName,
                typeof(ModProxy).FullName
            );
            object result = proxy.InvokeStartMethod(dllFile);
            Console.WriteLine($"result: {result}");
        }
        finally
        {
            AppDomain.Unload(domain);
        }
    }
}

// 跨域代理类,必须继承MarshalByRefObject
public class ModProxy : MarshalByRefObject
{
    public object InvokeStartMethod(string dllPath)
    {
        Assembly assembly = Assembly.LoadFrom(dllPath);
        Type type = assembly.GetType("MainMod");
        if (type != null)
        {
            MethodInfo method = type.GetMethod("Start");
            return method.Invoke(null, null); // 静态方法无需实例
        }
        return null;
    }
}

内容的提问来源于stack exchange,提问作者Accis

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.29 20:57:37