Attribute, interface, or abstract class

不羁的心 提交于 2019-12-10 17:19:04

问题


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.


回答1:


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.




回答2:


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.




回答3:


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:

[assembly: PluginClass(typeof(MyPlugin), more info)]

You can then call GetCustomAttributes on the Assembly, which would probably be much faster than GetTypes.

Using LINQ:

filenames.SelectMany(f => 
        Assembly.LoadFrom(f).GetCustomAttributes(typeof(PluginClassAttribute), true)
        .Cast<PluginClassAttribute>()
        .Select(a => a.PluginType)
).ToList();


来源:https://stackoverflow.com/questions/2137399/attribute-interface-or-abstract-class

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!