属性、接口或抽象类
我想知道以下实现的一般建议是什么(属性、接口、抽象类或其组合):
/// <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)。
更新:基于下面的优秀建议、评论和链接,属性和界面的混合是最好的方法。属性使您可以安全地加载程序集并检查所需的信息和实现,而无需实例化插件类/对象。这在允许第三方或最终用户创建自定义插件的情况下是理想的选择。我们可以检查以确保正确的合约实现位于属性指定的位置。我们可以检查所需的依赖项和资源,并在实例化任何内容之前提醒开发人员任何问题。
I'm wondering what the general recommendation would be (attribute, interface, abstract class, or combination thereof) for the following implementation:
/// <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);
}
}
I realize there are advantages and disadvantages to each method. Obviously, attributes require some reflection (as do abstract extension and interface implementation). An abstract class takes our only base inheritance, and any future changes in an interface can break any existing plugins. So, as I see it, those are the disadvantages.
Performance is not an issue (unless there is something I don't see) since any reflection is only done once when a qualified class is extracted. The key pieces of data that are getting saved is a name for the plugin ("MyPlugIn"), the namespace ("SuperPlugIn.PlugInClass"), and the startup path for the .dll. Right now, with the abstract PlugIn class, the extension of the properties is enforced. This is more or less the same result if we implement an interface (IPlugIn).
We are allowing custom plugins to be written by end-users. With the plugins we are writing in-house, it is easy to teach and enforce a required structure for our application to instance a qualified class. However, I'm also considering the difficulties or inconvenience to the end-user should there be a major change.
All comments, suggestions, and questions welcome!!
Note: thanks go to Jon Skeet for the lambda expression in the snippet. :)
EDIT: I should have noted in the beginning that this is intended to be platform independent (i.e. Mono).
UPDATE: Based on the excellent recommendations, comments, and links below, a mix of attributes and interfaces is the best approach. Attributes let you load the assembly and check for required information and implementations rather safely without instancing the plugin classes/objects. This is ideal in situations where 3rd party or end users are allowed to create custom plugins. We can check to ensure that the proper contract implementation is in place where the attribute says it's suppose to be. We can check for required dependencies and resources and alert the developer of any problems before anything is instanced.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
您希望您的最终用户编写插件吗?我认为这不是一个好主意,除非您的最终用户是程序员。
这次我的回答会很简短,因为这是一个漂亮 大 honkin' dupe:
插件架构几乎总是涉及外部程序集中的类,在公共程序集中实现特定的接口;
已经有 数十个 cookie-cutter .NET 插件实现,包括 Microsoft 自己的 托管可扩展性框架。不要重新发明轮子。
编辑:对于 Mono,请查看 Mono.Addins。
You want your end users to write plugins? I don't think that's a very good idea, unless your end users are programmers.
I'm going to keep my answer short this time since this is a pretty big honkin' dupe:
Plug-in architectures almost always involve classes in an external assembly implementing a specific interface in a common assembly;
There are already dozens of cookie-cutter .NET plugin implementations including Microsoft's own Managed Extensibility Framework. Don't reinvent the wheel.
Edit: For Mono, check out Mono.Addins.
我可能倾向于使用属性。用元数据扩展基类系统正是它们的目的,并且说“这个类是一个插件”当然符合这个要求。
I'd probably tend to use attributes. Extending the base class system with metadata is kind of exactly what they're for, and saying 'this class is a plugin' certainly fits that bill.
Assembly.GetTypes
是一个非常昂贵的调用,我会尽可能避免它。 (应用程序启动时间很重要)更快的方法可能是(我没有进行基准测试)程序集级属性,它的使用方式如下:
然后您可以在
上调用
,这可能比GetCustomAttributes
>AssemblyGetTypes
快得多。使用 LINQ:
Assembly.GetTypes
is a very expensive call, and I would avoid it where possible. (App startup time matters)The faster way to do this is probably (I haven't benchmarked) an assembly-level attribute, which would be used like this:
You can then call
GetCustomAttributes
on theAssembly
, which would probably be much faster thanGetTypes
.Using LINQ: