我想知道以下实现的一般建议是什么(属性,接口,抽象类或它们的组合):
/// <summary>
/// Loads class specific information into a list for serialization. The class must extend PlugIn.
/// The filenames parameter is passed from a FileDialog.
/// </summary>
/// <param name="filenames">Accepts any number of filenames with fully qualified paths.</param>
public static void ExtractPlugInData(params string[] filenames)
{
List<Type> l;
foreach (string f in filenames)
{
Assembly a = Assembly.LoadFrom(f);
// lambda expression selects any class within a library extending the abstract PlugIn class
l = a.GetTypes().Where(type => typeof(PlugIn).IsAssignableFrom(type)).ToList<Type>();
if (l.Count > 0)
// write data to serializable class
WritePlugInData(f , l);
else
// throw exception
WriteLine("{0} :: No PlugIn Data Found" , a.FullName);
}
}
我意识到每种方法都有优点和缺点。显然,属性需要一些反思(抽象扩展和接口实现也是如此)。抽象类采用我们唯一的基本继承,接口中的任何未来更改都可以破坏任何现有插件。所以,正如我所看到的那样,这些都是缺点。
性能不是问题(除非有一些我看不到的东西)因为任何反射只在提取限定类时才进行一次。要保存的关键数据是插件的名称(“MyPlugIn”),命名空间(“SuperPlugIn.PlugInClass”)以及.dll的启动路径。现在,使用抽象的PlugIn类,强制执行属性的扩展。如果我们实现一个接口(IPlugIn),这或多或少是相同的结果。
我们允许最终用户编写自定义插件。使用我们内部编写的插件,可以很容易地为我们的应用程序教授和实施一个必需的结构来实例化一个合格的类。但是,如果发生重大变化,我也在考虑给最终用户带来的困难或不便。
欢迎所有评论,建议和问题!
注意:感谢Jon Skeet获取片段中的lambda表达式。 :)
编辑:我应该在开始时注意到这是与平台无关的(即Mono)。
更新:根据下面的优秀建议,评论和链接,混合属性和界面是最好的方法。通过属性,您可以加载程序集并相当安全地检查所需的信息和实现,而无需实例化插件类/对象。这在允许第三方或最终用户创建自定义插件的情况下非常理想。我们可以检查以确保正确的合同实施到位,其中属性表示它是假设的。我们可以检查所需的依赖项和资源,并在任何实例出现之前提醒开发人员任何问题。
答案 0 :(得分:2)
您希望最终用户编写插件吗?除非你的最终用户是程序员,否则我认为这不是一个好主意。
这次我会尽量缩短答案,因为这是pretty big honkin' dupe:
插件架构几乎总是涉及在通用程序集中实现特定接口的外部程序集中的类;
已有dozens个cookie-cutter个.NET plugin实施,包括微软自己的Managed Extensibility Framework。不要重新发明轮子。
编辑:对于Mono,请查看Mono.Addins。
答案 1 :(得分:1)
Assembly.GetTypes
是一个非常昂贵的电话,我会尽可能避免它。 (App启动时间很重要)
执行此操作的更快方法可能是(我没有基准测试)一个程序集级属性,可以像这样使用:
[assembly: PluginClass(typeof(MyPlugin), more info)]
然后,您可以在GetCustomAttributes
上致电Assembly
,这可能会比GetTypes
快得多。
使用LINQ:
filenames.SelectMany(f =>
Assembly.LoadFrom(f).GetCustomAttributes(typeof(PluginClassAttribute), true)
.Cast<PluginClassAttribute>()
.Select(a => a.PluginType)
).ToList();
答案 2 :(得分:1)
我可能倾向于使用属性。使用元数据扩展基类系统正是他们的目的,并且说“这个类是一个插件”当然适合这个法案。