代码之家  ›  专栏  ›  技术社区  ›  Christopher Painter

为什么我在使用反射后在.NET7中出现强制转换错误?

  •  -1
  • Christopher Painter  · 技术社区  · 3 年前

    反射和投射在.NET7和.NETFramework中是否不同?我正在移植一个项目,并在移动代码后得到这个转换错误。更糟糕的是,这个类实现了那个接口。这段代码适用于.NET 4.x。

        foreach (Assembly pluginAssembly in pluginAssemblies)
        {
            try
            {
                // Look for class(s) with our interface and construct them
                foreach (var type in pluginAssembly.GetTypes())
                {
                    Type iDesigner = type.GetInterface(typeof(IFireworksDesigner).FullName);
                    if (iDesigner != null)
                    {
                        object instance = Activator.CreateInstance(type); // creates an object 
                        IFireworksDesigner designer  = (IFireworksDesigner)instance; // throws an exception
                        // do stuff
                    }
                }
            }
            catch(Exception ex)
            {
                //Something really bad must have happened. 
                MessageBox.Show("Fatal error reflecting plugins in assembly '" + pluginAssembly.FullName + "'.\r\n" +
                    "The error message is:\r\n\r\n" + ex.Message);
            }
        }
    

    更新:我在 https://github.com/chrpai/reflection

    使用FW48 EXE调用.NET Standard 2.0即可工作。 如果Core7 EXE调用.NET Standard 2.0或.NET 7 DLL,则会失败。

    0 回复  |  直到 3 年前
        1
  •  3
  •   shingo    3 年前

    我得到了原因,结论是类实现的接口和你试图转换的接口存在于不同的 AssemblyLoadContext s、 所以它失败了。


    详细信息

    The documentation of LoadFile 说:

    LoadFile不会将文件加载到加载自上下文中

    这意味着 加载文件 将程序集加载到新上下文中, the source code 也验证了这一点。

    AssemblyLoadContext alc = new IndividualAssemblyLoadContext($"Assembly.LoadFile({normalizedPath})");
    result = alc.LoadFromAssemblyPath(normalizedPath);
    

    加载后 LibCore.dll 通过 加载文件 ,共有2个 程序集加载上下文 s存在于程序中,它们都有一个接口 IPlugin 具有相同的名称和相同的程序集名称。所以代码 type.GetInterface(typeof(IPlugin).FullName) 甚至使用 AssemblyQualifiedName 无法区分不同的上下文。

    使用以下代码进行验证。

    // IndividualAssemblyLoadContext #2
    Console.WriteLine(AssemblyLoadContext.GetLoadContext(iDesigner.Assembly));
    // DefaultAssemblyLoadContext #0
    Console.WriteLine(AssemblyLoadContext.GetLoadContext(typeof(IPlugin).Assembly));
    

    解决方案1

    使用 LoadFrom 而不是 加载文件 加载自 将程序集加载到“从上下文加载”中,以便可以共享接口。

    pluginAssemblies.Add(Assembly.LoadFrom(file));
    

    解决方案2

    使用一个单独的核心库,例如,您可以有3个项目。

    ConsoleCoreCore.exe
    
    LibCore.dll
        public interface IPlugin
        public class Server
    
    Lib1.dll
        public class LibStandardPlugin : IPlugin
    

    二者都 库1.dll 控制台核心.exe 提到 LibCore.dll

    ConsoleCoreCore.exe -> LibCore.dll <- Lib1.dll
    

    现在,如果您加载 库1.dll 从…起 LibCore.dll 通过 加载文件 , 库1.dll 将加载到一个单独的上下文中,但它 插件接口 接口仍然是 LibCore.dll

    // IndividualAssemblyLoadContext #1
    Console.WriteLine(AssemblyLoadContext.GetLoadContext(type.Assembly));
    // DefaultAssemblyLoadContext #0
    Console.WriteLine(AssemblyLoadContext.GetLoadContext(iDesigner.Assembly));
    // DefaultAssemblyLoadContext #0
    Console.WriteLine(AssemblyLoadContext.GetLoadContext(typeof(IPlugin).Assembly));
    
    推荐文章