如果我从加载的 dll 文件而不是主应用程序访问它,为什么静态值不同?

Why is a static value different if I access it from a loaded dll file instead of the main application?

我正在试验一个插件系统。这样就可以从文件夹中读取 DLL 文件,然后将这些函数添加到主应用程序中。

所有 DLL 文件都包含接口 IPlugin(项目:插件):

public interface IPlugin
{
    void SetValue();
    string GetValue();
}

插件在单独的项目中:

public class PluginAB : IPlugin
{
    public string GetValue()
    {
        return SomeStaticValues.StaticValues.Value;
    }

    public void SetValue()
    {
        SomeStaticValues.StaticValues.Value = "AB";
    }
}

有两个“插件”AB 和 XY(区别仅在于写“XY”而不是“AB”)。

他们所做的只是在另一个项目中写入一个静态值:

public static class StaticValues
{
    public static string Value { get; set; }
}

参考资料如下图所示:

References

DLL 中的 IPlugin 对象是使用 InvokeMember() 创建的:

private IPlugin LoadPlugin(string iPath)
    {
        Assembly lAssambly = Assembly.LoadFrom(iPath);

        foreach (Type lType in lAssambly.GetTypes())
        {
            if (lType.GetInterfaces().Contains(typeof(IPlugin)))
            {
                return lType.InvokeMember(null, BindingFlags.CreateInstance, null, null, null) as IPlugin;
            }
        }

        return null;
    }

正如您在参考图片中看到的,主应用程序也可以访问“StaticValues”class。

如果我这样调用主应用程序中的方法:

static void Main(string[] args)
    {
        IPlugin lPluginAB = LoadPlugin("AB");
        IPlugin lPluginXY = LoadPlugin("XY");

        lPluginAB.SetValue();
        Console.WriteLine(lPluginAB.GetValue());
        lPluginXY.SetValue();
        Console.WriteLine(lPluginAB.GetValue());
        SomeStaticValues.StaticValues.Value = "Test";
        Console.WriteLine(lPluginAB.GetValue());

        Console.ReadLine();
    }

我预计:

AB XY Test

相反,我得到:

AB XY XY

如果我直接检查

Console.WriteLine(SomeStaticValues.StaticValues.Value);

我得到

Test

如果我从加载的 dll 文件而不是主应用程序访问它,为什么静态值不同?

当使用 Assembly.LoadFrom(path) 方法“手动”加载程序集时,它们通常还会检索其引用的程序集。除非它们在中央存储库中注册 and/or,否则引用的程序集将来自加载的 dll 本身所在的同一文件夹。

如果 main 方法也引用了这样的程序集,它将从自己的文件夹中检索它,我怀疑您的情况与插件的来源不同。

在内部,从不同位置加载的 dll 被视为不同的、独立的程序集,而不是同一个程序集,因此它们每个都有自己的静态变量。

您可以使用以下代码检查是否发生这种情况:

using System.Reflection;
using System.Text;

        public static string reportCurrentlyLoadedAssemblies()
        {
            StringBuilder sb = new StringBuilder();
            Assembly[] loadedAssemblies = AppDomain.CurrentDomain.GetAssemblies();
            foreach (Assembly ass in loadedAssemblies)
            {
                sb.Append(ass.FullName);
                sb.Append(";");
                if (!ass.IsDynamic)
                {
                    sb.Append(ass.CodeBase);
                    sb.Append(";");
                }
                sb.Append(ass.Location);
                sb.AppendLine(";");
            }
            return sb.ToString();
        }

在您的 Main 方法结束时调用此方法并检查结果。

在这种情况下,我发现预加载通用程序集可以防止插件检索它们自己的版本。您可以通过在 Main 方法开始时使用 SomeStaticValues 调用 Assembly.LoadFrom(path) 来显式预加载它们;也许一开始访问 SomeStaticValues.StaticValues.Value 就足够了。

另一种可能性是安排插件的 dll 出现在 Main 项目的目标文件夹中并从那里加载它们,以便它们将使用相同的源 SomeStaticValues 作为 Main 方法。

到底改

Assembly lAssambly = Assembly.LoadFrom(lFile.FullName);

Assembly lAssambly = Assembly.LoadFile(lFile.FullName);

成功了。

找到了 here