为什么相同的类型不相等?
我正在开发一个小插件框架,并注意到我无法成功使用Type.IsAssignableFrom()
来检查插件类是否实现了类型IPlugin
。 我检查了其他SO问题(比如这个) ,看看为什么函数返回false,但令我惊讶的是,没有一个建议有效。
我在插件程序PluginAPI.IPlugin
有一个类,它在引用的程序PluginAPI.IPlugin
实现PluginAPI.IPlugin
类。 检查我的PluginAPI.IPlugin
类型的AssemblyQualifiedName
以及插件类的接口列表的类型时,我发现没有任何区别。 两者都超出了价值:
PluginAPI.IPlugin,PluginAPI,Version = 1.0.0.0,Culture = neutral,PublicKeyToken = null
经过进一步调查,我检查了Type.cs源代码 ,发现函数IsAssignableFrom()
在内部调用中失败,它检查类型是否相等(via ==
)。
我发现修改我的插件框架以在典型的执行上下文( Assembly.LoadFrom
)中加载插件程序集而不是仅reflection上下文( Assembly.ReflectionOnlyLoadFrom
)允许类型相等性检查评估为true ,这是我所期望的一直。
为什么仅reflection上下文导致类型不再相等?
码:
以下部分包含用于复制问题的相关代码以及已设置的项目说明。
程序集1:PluginAPI
该程序集仅包含IPlugin.cs:
namespace PluginAPI { public interface IPlugin { } }
程序集2:Plugin1
该程序集包含几个插件(实现PluginAPI.IPlugin
类)。
namespace Plugin1 { public class MyPlugin : IPlugin { public MyPlugin() { } } }
程序集3:AppDomainTest
此程序集包含入口点并测试将插件程序集加载到单独的AppDomain中,以便可以卸载它们。
namespace AppDomainTest { public class AppDomainTest { public static void Main(String[] args) { AppDomain pluginInspectionDomain = AppDomain.CreateDomain("PluginInspectionDomain"); PluginInspector inspector = new PluginInspector(); pluginInspectionDomain.DoCallBack(inspector.Callback); AppDomain.Unload(pluginInspectionDomain); Console.ReadKey(); } } }
/// /// If using this class. You can comment/uncomment at the lines provided /// where Assembly.Load* vs. Assembly.ReflectionOnlyLoad* are used. /// You should notice Assembly.Load* succeeds in determining type equality; /// however, Assembly.ReflectionOnlyLoad* fails. /// namespace AppDomainTest { [Serializable] public class PluginInspector { public void Callback() { AppDomain.CurrentDomain.ReflectionOnlyAssemblyResolve += CurrentDomain_ReflectionOnlyAssemblyResolve; //TODO: Change this to the output directory of the Plugin1.dll. string PluginDirectory = @"H:ProjectsSOTestPlugin1binDebug"; DirectoryInfo dir = new DirectoryInfo(PluginDirectory); if (dir.Exists) { FileInfo[] dlls = dir.GetFiles("*.dll"); //Check if the dll has a "Plugin.config" and if it has any plugins. foreach (FileInfo dll in dlls) { LoadAssembly(dll.FullName); } } AppDomain.CurrentDomain.ReflectionOnlyAssemblyResolve -= CurrentDomain_ReflectionOnlyAssemblyResolve; } private void LoadAssembly(string path) { //From within the PluginInspectionDomain, load the assembly in a reflection only context. Assembly[] loadedAssemblies = AppDomain.CurrentDomain.ReflectionOnlyGetAssemblies(); //TODO (toggle comment): Assembly[] loadedAssemblies = AppDomain.CurrentDomain.GetAssemblies(); AssemblyName assemblyName = AssemblyName.GetAssemblyName(path); bool assemblyAlreadyLoaded = loadedAssemblies.Any(new Func((Assembly a) => { //If the assembly full names match, then they are identical. return (assemblyName.FullName.Equals(a.FullName)); })); if (assemblyAlreadyLoaded) { //Assembly already loaded. No need to search twice for plugins. return; } //Assembly not already loaded, check to see if it has any plugins. Assembly assembly = Assembly.ReflectionOnlyLoadFrom(path); //TODO (toggle comment): Assembly assembly = Assembly.LoadFrom(path); GetPlugins(assembly); } private Assembly CurrentDomain_ReflectionOnlyAssemblyResolve(object sender, ResolveEventArgs args) { //This callback is called each time the current AppDomain attempts to resolve an assembly. //Make sure we check for any plugins in the referenced assembly. Assembly assembly = Assembly.ReflectionOnlyLoad(args.Name); //TODO (toggle comment): Assembly assembly = Assembly.Load(args.Name); if (assembly == null) { throw new TypeLoadException("Could not load assembly: " + args.Name); } GetPlugins(assembly); return assembly; } /// /// This function takes an assembly and extracts the Plugin.config file and parses it /// to determine which plugins are included in the assembly and to check if they point to a valid main class. /// /// public List GetPlugins(Assembly assembly) { using (Stream resource = assembly.GetManifestResourceStream(assembly.GetName().Name + ".Plugin.config")) { if (resource != null) { //Parse the Plugin.config file. XmlSerializer serializer = new XmlSerializer(typeof(PluginConfiguration)); PluginConfiguration configuration = (PluginConfiguration)serializer.Deserialize(resource); if (configuration == null) { Console.WriteLine("Configuration is null."); } if (configuration.Plugins == null) { Console.WriteLine("Configuration contains no plugins."); } foreach (Plugin pluginDescriptor in configuration.Plugins) { bool containsType = false; foreach (Type type in assembly.GetExportedTypes()) { if (type.FullName.Equals(pluginDescriptor.MainClass)) { containsType = true; if (typeof(IPlugin).IsAssignableFrom(type)) { Console.WriteLine("MainClass '{0}' implements PluginAPI.IPlugin", pluginDescriptor.MainClass); } Console.WriteLine("Checking for {0}", typeof(IPlugin).AssemblyQualifiedName); Console.WriteLine("Interfaces:"); foreach (Type interfaceType in type.GetInterfaces()) { Console.WriteLine("> {0}", interfaceType.AssemblyQualifiedName); if (interfaceType == typeof(IPlugin)) { //This is NOT executed if in reflection-only context. Console.WriteLine("interface is equal to IPlugin"); } } } } Console.WriteLine((containsType ? "Found '" + pluginDescriptor.MainClass + "' inside assembly. Plugin is available." : "The MainClass type could not be resolved. Plugin unavailable.")); } } } return null; } } }
如果对我如何使用XML指定插件配置感到好奇,请查看这些文件。 请注意,重新解决问题不需要XML解析。 例如,你可以硬编码一些类型的字符串来搜索和比较,但这就是我选择这样做的方式。 这也为那些有兴趣复制我的插件加载器的人提供了参考。
XML Schema :PluginConfiguration.xsd
Matches class names that follow the format: IDENTIFIER(.IDENTIFIER)* This pattern is necessary for validating fully qualified class paths.
PluginConfiguration.cs
namespace PluginAPI { /// [XmlType(AnonymousType = true, Namespace = "clr-namespace:PluginAPI;assembly=PluginAPI/PluginConfiguration.xsd")] [XmlRoot(ElementName = "PluginConfiguration", Namespace = "clr-namespace:PluginAPI;assembly=PluginAPI/PluginConfiguration.xsd", IsNullable = false)] public partial class PluginConfiguration { private Plugin[] pluginField; private string authorField; private string versionField; /// [XmlElement("Plugin", typeof(Plugin))] public Plugin[] Plugins { get { return this.pluginField; } set { this.pluginField = value; } } /// [XmlAttribute()] public string Author { get { return this.authorField; } set { this.authorField = value; } } /// [XmlAttribute()] public string Version { get { return this.versionField; } set { this.versionField = value; } } } /// [XmlType(AnonymousType = true, Namespace = "clr-namespace:PluginAPI;assembly=PluginAPI/PluginConfiguration.xsd")] public partial class Plugin { private string mainClassField; private string nameField; /// [XmlAttribute()] public string MainClass { get { return this.mainClassField; } set { this.mainClassField = value; } } /// [XmlAttribute()] public string Name { get { return this.nameField; } set { this.nameField = value; } } } }
Plugin.config (插件程序集中的嵌入式资源)。
上述就是C#学习教程:为什么相同的类型不相等?分享的全部内容,如果对大家有所用处且需要了解更多关于C#学习教程,希望大家多多关注—计算机技术网(www.ctvol.com)!
本文来自网络收集,不代表计算机技术网立场,如涉及侵权请联系管理员删除。
ctvol管理联系方式QQ:251552304
本文章地址:https://www.ctvol.com/cdevelopment/959643.html