4

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.

IAbstract
  • 19,551
  • 15
  • 98
  • 146
  • I'm pretty sure it won't be an interface. – Jay Jan 26 '10 at 03:44
  • A lot of the references given by Aaronaught end up using interfaces. Amazingly enough Mono.Addins (http://www.mono-project.com/Mono.Addins) goes the attribute route. – IAbstract Jan 26 '10 at 05:11
  • It's true, that's why I qualified my answer originally with the word "almost." ;) There's no *right* or *wrong* answer and you can even combine both approaches (use attributes to find the classes but still have them implement a plugin interface). – Aaronaught Jan 26 '10 at 05:41
  • @Aaronaught: we may do a combination Attribute & interface...at this point I'm not completely sure. But I am fairly certain that we'll move forward with the Attribute as our primary condition. – IAbstract Jan 26 '10 at 07:09

3 Answers3

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.

Community
  • 1
  • 1
Aaronaught
  • 120,909
  • 25
  • 266
  • 342
  • Apologies for the pretty big honkin' dupe. I guess I failed to search on 'plugin'. ;/ Anyhoo...I'm looking at the links you provided. – IAbstract Jan 26 '10 at 04:16
  • @dboarman: Don't worry about it, I meant for it to sound light-hearted, I guess it came off as snarky instead. I'm not dishin' out downvotes or anything, just wanted to give a clear reason for my brief answer and let you know that it's a fairly common requirement and that there are many good resources on the topic. – Aaronaught Jan 26 '10 at 04:23
  • :) no worries...you had some great references. Only problem is that MEF is available in .NET 4.0 and since we are aiming towards Mono, this won't be possible. BTW, +1 for the great references!!! – IAbstract Jan 26 '10 at 05:16
  • @dboarman: Didn't realize you were targeting Mono. Too bad that you won't be able to use MEF, but Mono.Addins might help you (link added to answer). – Aaronaught Jan 26 '10 at 05:26
  • Thanks for adding the link...I added it in a comment to Jay as well... :) There is also a benefit to 'rolling our own' plugin framework in that I get to learn a whole lot. – IAbstract Jan 26 '10 at 05:31
  • @dboarman: Learning is a great goal and I'd absolutely encourage building this sort of thing for fun/general knowledge; just be careful not to mix educational code with production code if you don't have to. – Aaronaught Jan 26 '10 at 05:43
  • @Aaronaught: our educational code is actually maturing at a rapid pace. We are moving forward with an Attribute/interface mix. With the attributes we are able to determine a lot of configuration properties up-front without having to actually instance any classes other than the attribute class(es). Then we are able to determine inheritances/implementations and build an organized view of an end-user's custom plugin. – IAbstract Jan 31 '10 at 02:19
  • @dboarman: Good to hear! I kind of figured it would turn out to be a mix, much like what Mono does. Attributes as configuration, interfaces as contracts. Hopefully the end result turns out well for you. – Aaronaught Jan 31 '10 at 02:31
1

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();
SLaks
  • 868,454
  • 176
  • 1,908
  • 1,964
  • What happens if there is an exception being thrown for one of them? – Hamish Grubijan Jan 26 '10 at 03:59
  • If an exception occurs while loading an assembly, the `ToList` call will throw, and you won't get anything. If you want to use a `foreach` with a `catch` block, feel free. – SLaks Jan 26 '10 at 04:06
  • I've already tested for libraries that don't extend my current use of PlugIn. As you can see in the snippet I gave, I call ToList. If there are no extension of my PlugIn, the list has a count of 0. Keep in mind that the Assembly.GetTypes is only called when a library is first loaded. I extract any pertinent data for the Activator.CreateInstance to do it's job. – IAbstract Jan 26 '10 at 05:24
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.

kyoryu
  • 12,848
  • 2
  • 29
  • 33
  • @kyoryu: I'm beginning to lean towards the attributes. See my comment above for Mono.Addins. I think it's funny - MS says interface, Mono says attributes. – IAbstract Jan 26 '10 at 05:27