简体   繁体   English

为什么我在使用反射后在 .NET 7 中出现转换错误?

[英]Why am I getting a casting error in .NET 7 after using reflection?

Is reflection and casting different in .NET 7 vs .NET Framework? .NET 7 与 .NET 框架中的反射和投射是否不同? I'm porting a project and getting this casting error after moving the code over.我正在移植一个项目并在移动代码后收到此转换错误。 The wierd thing is this class implements that interface.奇怪的是这个 class 实现了那个接口。 This code works in .NET 4.x.此代码适用于 .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);
        }
    }

Update: I've made a sample repos at https://github.com/chrpai/reflection更新:我在https://github.com/chrpai/reflection做了一个样本回购

With a FW48 EXE calling .NET Standard 2.0 it works.使用调用 .NET Standard 2.0 的 FW48 EXE,它可以工作。 With a Core7 EXE calling either a .NET Standard 2.0 or .NET 7 DLL it fails.使用调用 .NET Standard 2.0 或 .NET 7 DLL 的 Core7 EXE 失败。

I got the reason, the conclusion is the interface the class implemented and the interface you try to cast to exist in different AssemblyLoadContext s, so it fails.我明白了,结论是 class 实现的接口和您尝试转换为存在于不同的AssemblyLoadContext中的接口,因此它失败了。


Details细节

The documentation of LoadFile says: LoadFile的文档说:

LoadFile does not load files into the load-from context LoadFile 不会将文件加载到加载源上下文中

That means LoadFile will load an assembly into a new context, the source code also verifies this.这意味着LoadFile会将程序集加载到新的上下文中, 源代码也验证了这一点。

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

After you load LibCore.dll by LoadFile , there are 2 AssemblyLoadContext s exist in the program, both of them have an interface IPlugin with the same name and same assembly name.通过LoadFile加载LibCore.dll后,程序中存在 2 个AssemblyLoadContext ,它们都有一个具有相同名称和相同程序集名称的接口IPlugin So the code type.GetInterface(typeof(IPlugin).FullName) even using AssemblyQualifiedName is not able to distinguish between different contexts.所以代码type.GetInterface(typeof(IPlugin).FullName)即使使用AssemblyQualifiedName也无法区分不同的上下文。

Using the following code to verify.使用下面的代码来验证。

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

Solution 1解决方案 1

Use LoadFrom instead of LoadFile .使用LoadFrom而不是LoadFile LoadFrom loads an assembly into load-from context, so the interface can be shared. LoadFrom将程序集加载到加载源上下文中,因此可以共享接口。

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

Solution 2方案二

Use an individal core library, for example you can have 3 projects.使用单独的核心库,例如您可以有 3 个项目。

ConsoleCoreCore.exe

LibCore.dll
    public interface IPlugin
    public class Server

Lib1.dll
    public class LibStandardPlugin : IPlugin

Both Lib1.dll and ConsoleCoreCore.exe refer to LibCore.dll Lib1.dllConsoleCoreCore.exe都是LibCore.dll

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

Now if you load Lib1.dll from LibCore.dll by LoadFile , Lib1.dll will be loaded into an individal context, but its IPlugin interface is still the one from LibCore.dll .现在,如果您通过LoadFileLibCore.dll加载Lib1.dll ,则Lib1.dll将被加载到一个单独的上下文中,但其IPlugin接口仍然是来自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));

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM