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.