我正在考虑为我目前正在开发的应用程序的未来插件创建设计一个适当的合同。基本上这个想法是定义一个接口,但我希望应用程序知道当前呈现给系统的插件,并向用户显示一个很好的插件列表,其中包含它们的名称和简短的描述,当然插件的开发人员应该提供,应用程序的用户不应该能够轻易地改变它,所以额外的配置文件不是一个选项。我不想为此使用程序集文件名的类名。另外我认为它应该可以在不实例化插件的情况下访问,但可以通过反射来访问,例如:assembly.GetType(type).GetProperty("Name").GetValue(null, null).ToString();
. 当然,我可以提供一些逻辑来检查是否存在,例如if(assembly.GetType(type).GetProperty("Name") != null)
,但这也不是一个好主意,因为如果该属性不存在,最终用户将不知道该插件的作用,甚至不知道它的名称是什么。现在,它的行为应该像一个静态属性,但静态是不可覆盖的,所以似乎我不能将它声明为接口的一部分,也不能在抽象类中声明。也许我走错了路,它看起来只是一个静态属性,我可以通过另一种方法实现这个功能。所以简短的问题可能是“如何强制第三方开发人员提供一些关于他的插件的元信息”。请指教。
3 回答
您可以尝试使用两个接口:
IAddIn
for 是所有加载项都将实现的主界面。
IAddInInfo
作为提供插件元数据(名称、发布者、描述版本等)的接口
每个加载项都应该实现这两个。一个IAddInInfo
实现可能是这样的:
public class ScannerAddInInfo : IAddInInfo
{
public string Name { get { return "Scanner"; } }
public string Description { get { return "Add-in for acquiring images from a scanner device"; } }
}
为了确保加载项的所有实现都带有元数据,您可以创建IAddIn
一个通用接口,例如:
public interface IAddIn<T> where T : IAddInInfo
{
T Info { get; }
//Continue with the rest of the members you would want every add-in to have.
}
那么扫描仪插件实现将是:
public class ScannAddIn : IAddIn<ScannerAddInInfo>
{
private ScannerAddInInfo _info = new ScannerAddInInfo();
public ScannerAddInInfo Info { get { return _info; } }
//Continue with the rest of the IAddIn implementation.
}
然后,您可以从一个特殊的加载项文件夹加载加载项程序集,并创建实现类型的实例,IAddInInfo
并在您的应用程序中显示来自已发现加载项的信息。请注意,尚未创建任何加载项。为此,您需要添加更多反射来查找实现的类型IAddIn<ScannerAddInInfo>
。
为了使这更简单,您可以将加载项类型名称添加到IAddInInfo
接口或类似的东西。
这种方法的唯一缺点是您必须加载在您的特殊加载项文件夹中找到的所有程序集,即使它们不包含任何加载项。
为避免这种情况,您可以尝试Mono.Cecil。然后你将不得不做这样的事情:
AssemblyDefinition ad = AssemblyDefinition.ReadAssembly(assemblyPath);
foreach (TypeDefinition td in ad.MainModule.GetTypes())
{
if (td.BaseType != null && td.BaseType.FullName == "MyNamespace.MyAddInBase")
{
return true;
}
}
要加载程序集,您可以使用Assembly.LoadForm并创建加载项和加载项信息的实例,这是Activator.CreateInstance重载之一。
祝你好运。
要向插件添加一些“元数据”,您可以使用属性。为您的插件创建一个自定义属性,并通过反射读出信息并将其显示在您的应用程序中。
有关属性的更多信息:
您应该做的事情基本上是定义您的接口并期望其他人实现他们的具体类并以某种方式将该运行时对象提供给您(您的预定义方法、配置等)
但是有一些称为依赖注入的机制。这允许你定义你的接口和你的入口点,而一个“系统”负责匹配你的入口点和实现者的具体细节。为此目的有“System.ComponentModel.Composition”命名空间。
我知道有一个名为“Unity”的框架在做这样的工作。我猜组合命名空间在某种程度上是统一的简化版本。您可以查看“ImportAttribute”和“ExportAttribute”类的帮助以获取一些提示。