如果我从加载的 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
我正在试验一个插件系统。这样就可以从文件夹中读取 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