4
\$\begingroup\$

I have been developing this plugin architecture, mostly for fun and education, its a simple WinForm with some plugin logic and two plugins, i want to know if there is a better way of doing it.

Form1.cs

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace Plugin_architecture
{
    public partial class Form1 : Form
    {
    // -----------------------Plugin logic ----------------------------------

    private static IEnumerable<Type> getDerivedTypesFor(Type baseType)
    {
        var assembly = Assembly.GetExecutingAssembly();
        return assembly.GetTypes().Where(baseType.IsAssignableFrom).Where(t => baseType != t);
    }

    public void invokePlugin(object sender, EventArgs e, string pluginIdentifier) {
        Type type = Type.GetType(pluginIdentifier);
        object instance = Activator.CreateInstance(type);
        MethodInfo method = type.GetMethod("start");
        method.Invoke(instance, null);
    }

    public void registerPlugins() {
        // Register plugins at application startup
        IEnumerable<Type> plugins = getDerivedTypesFor(typeof(Plugin));

        foreach(Type plugin in plugins) {

            LinkLabel pluginLabel = new LinkLabel();
            pluginLabel.Text = plugin.ToString();
            pluginLabel.Click += delegate(object sender, EventArgs e) { invokePlugin(sender, e, plugin.ToString()); };
            pluginContainer.Controls.Add(pluginLabel);

        }
    }
    // --------------------------------------------------------------------------

    public Form1()
    {
        InitializeComponent();
        registerPlugins();
        }
    }
}

Plugin.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

abstract class Plugin
{
/*
 * Base class for plugins
 * Implement the start method and add any functionality you want. The start method will be
 * called each time the plugin is started. Use the stop method to do any nessesary cleanup.
 */

//public void init() { 
//    // Initialize plugin
//}

//public void register() { 
//    // Register plugin in application
//}

abstract public void start();
abstract public void stop();
}

ServicePlugin.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

class ServicePlugin : Plugin
{
override public void start() { 

    const string message = "Yeahh! Plugins!!";
    const string caption = "Plugins!";
    var result = MessageBox.Show(message, caption,
                                 MessageBoxButtons.OK,
                                 MessageBoxIcon.Information);
}
override public void stop() { 

}
}

ClientPlugin.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using Plugin_architecture.Plugins.ClientPlugin;

class ClientPlugin : Plugin
{
override public void start() {

    Client frm = new Client();
    frm.Show();
}
override public void stop() { 

    }
}

Client.cs

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace Plugin_architecture.Plugins.ClientPlugin
{
    public partial class Client : Form
    {
        public Client()
        {
            InitializeComponent();
        }
    }
}

The code works great, and should compile without problems.

\$\endgroup\$

2 Answers 2

3
\$\begingroup\$

You should use an interface instead of an abstract class:

public interface IPlugin {
    void Start();
    void Stop();
}

There are many reasons for this, as detailed in https://stackoverflow.com/questions/56867/interface-vs-base-class

I would also separate the plugin logic out of the form, and create a "PluginResolver" class to handle these concerns (This is where you hide the ugly reflection stuff you don't want to touch again).

In terms of the rest of the code, it's pretty basic, so not much to add.

In terms of creating a "plugin architecture" it's kind of a waste without using dependency injection (or a dependency resolver of some kind), because there's no obvious way for plugins to communicate with each other, unless they get coupled together (which defeats the whole point of the plugin architecture), instead by having a set of root services that can be injected into the particular plugins, you can have a disconnected way of giving them all access to say "the primary error logger".

\$\endgroup\$
3
\$\begingroup\$

Take a look at MEF. For example:

// This would preferably be in an assembly outside the main app
[InheritedExport]
public abstract class Plugin
{
    public abstract void Start();
    public abstract void Stop();
}

// This can be in an assembly outside the main app
public class MyPlugin : Plugin
{
    public override void Start()
    {
        // whatever
    }

    public override void Stop()
    {
        // whatever
    }
}

public class CompositionRoot
{
    public CompositionRoot()
    {
        var catalog = new AggregateCatalog();
        catalog.Catalogs.Add(new DirectoryCatalog("PluginsPath"));

        var container = new CompositionContainer(catalog);
        container.ComposeParts(this);
    }

    [ImportMany(typeof(Plugin))]
    public IEnumerable<Plugin> Plugins { get; set; }

    public void StartPlugins()
    {
        foreach (var plugin in Plugins)
        {
            plugin.Start();
        }
    }

    public void StopPlugins()
    {
        foreach (var plugin in Plugins)
        {
            plugin.Stop();
        }
    }
}
\$\endgroup\$

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.