14

I have a .net-app that provides a mechanism to extend the app with plugins. Each plugin must implement a plugin-interface and must provide furthermore a constructor that receives one parameter (a resource context).

During the instantiation of the plugin-class I look via reflection, if the needed constructor exists and if yes, I instantiate the class (via Reflection). If the constructor does not exists, I throw an exception that says that the plugin not could be created, because the desired constructor is not available.

My question is, if there is a way to declare the signature of a constructor in the plugin-interface so that everyone that implements the plugin-interface must also provide a constructor with the desired signature. This would ease the creation of plugins.

I don’t think that such a possibility exists because I think such a feature falls not in the main purpose for what interfaces were designed for but perhaps someone knows a statement that does this, something like:

public interface IPlugin {
    ctor(IResourceContext resourceContext);
    int AnotherPluginFunction();
}

I want to add that I don't want to change the constructor to be parameterless and then set the resource-context through a property, because this will make the creation of plugins much more complicated. The persons that write plugins are not persons with deep programming experience. The plugins are used to calculate statistical data that will be visualized by the app.


Thanks for all the answers.

I’ve decided, that I let it be an interface because I don’t like to force the plugin-programmers to inherit from an abstract class so that he or she loses the possibility to inherit from an own base-class. Furthermore, deriving from an abstract class does not ensure that the plugin programmer really provides the needed constructor. It makes it only more probable (The programmer has still the possibility to add only one constructor that contains the desired parameter but that also has additional parameters, and that’s also bad. See the comments to the answer of Ken Browning).

Although I mentioned in my post that I don’t want such a property, I marked the answer of Danny Varod as accepted because I think in my situation it’s the most appropriate solution. Thanks to all who answered.

Mark Elliot
  • 75,278
  • 22
  • 140
  • 160
HCL
  • 36,053
  • 27
  • 163
  • 213

7 Answers7

7

Interfaces cannot declare constructors. You might consider using an abstract class instead.

Ken Browning
  • 28,693
  • 6
  • 56
  • 68
  • Abstract classes can't enforce creation of constructors in their subclasses either though, can they? – Matthew Scharley Jun 09 '10 at 22:01
  • 1
    They can, if you define a ctor with parameters, which disables the automatic creation of a parameterless ctor. by this, you enforce ctor-chaining. You simply can't derive from a class without calling a basector unless the base class has a parameterless ctor. – Femaref Jun 09 '10 at 22:03
  • 1
    @Matthew Scharley, a sub class must always 'call' the constructor in a parent, hence if the parent has no parameterless constructor, the child must, somehow, get the parameters to it in it's constructor. This may be by enforcing the same constructor on the child, or maybe not, but it is a way to ensure the information required by the parent is given – johnc Jun 09 '10 at 22:04
  • 1
    @JS Bangs. No you can't http://stackoverflow.com/questions/504977/why-cant-i-create-an-abstract-constructor-on-an-abstract-c-class – johnc Jun 09 '10 at 22:05
  • An abstract class still wouldn't force its derived class to expose a constructor matching a signature. The derived class can always have it's own constructors passing the appropriate (or a default) data to the base class' constructor. Even if the base class only has 1 constructor which requires, for instance, a string parameter, a derived class can still do this: `public DerivedClass() : base("defaultValue")` or `public DerivedClass(string a, int b, DateTime c) : base(a)` So as you can see, the base class can't force the derived class to expose a constructor with a defined signature. – Matheus Rocha Jun 17 '23 at 03:21
7

Plug-in extendability is a favorite of mine...

What I do is make sure the plug-in either implements the interface or inherits the base class of the appropriate "plugin socket".

In some places base classes are more appropriate (if the plug-in is a kind of X),
in some interfaces are more appropriate (if the plug-in does IX).

I do not pass the context to the construct, instead I use a property for that and a parameterless public constructor.

This also enables easier deserialization of plug-ins using reflection.

Danny Varod
  • 17,324
  • 5
  • 69
  • 111
6

No, this does not exist. You are probably looking for an abstract class here.

Femaref
  • 60,705
  • 7
  • 138
  • 176
  • An abstract class still wouldn't force its derived class to expose a constructor matching a signature. The derived class can always have it's own constructors passing the appropriate (or a default) data to the base class' constructor. Even if the base class only has 1 constructor which requires, for instance, a string parameter, a derived class can still do this: `public DerivedClass() : base("defaultValue")` or `public DerivedClass(string a, int b, DateTime c) : base(a)` So as you can see, the base class can't force the derived class to expose a constructor with a defined signature. – Matheus Rocha Jun 17 '23 at 03:22
3

Alternatively, you might try using a factory: make the constructor signature a method signature of another type:

public abstract class PluginFactory
{
    public abstract IPlugin Create(IResourceContext context);
}

and then something like (and I always mess up this part if I want it to be short, hence the edit):

public class PluginContainer
{
    public IPlugin LoadPlugin<T>(IResourceContext context) where T: PluginFactory, new()
    {
        var factory = new T();
        return factory.Create(context);
    }
}
Arne
  • 750
  • 5
  • 10
  • Now that would work. The plugin factory could even be an interface, although you'd still need to implement it in a separate class than that of the plugin itself, as the interface can only enforce instance methods (not static ones). One thing I'd do is have the plugin interface enforce an `void Initialize(IResourceContext)` method. You'd then have to use the constraints `where T : IPlugin, new()`, and in the body of `LoadPlugin` you'd simply do `var plugin = new T(); plugin.Initialize(context); return plugin;`. This would eliminate the need for a factory type. – Matheus Rocha Jun 17 '23 at 03:33
1

Unfortunately, interfaces in C# can only contain methods, properties, events or indexers.

You could use and abstract class that all plugins would inherit from. You'd be able to force them to implement the constructor signature in that case.

Paul Kearney - pk
  • 5,435
  • 26
  • 28
1

The interface can't declare / enforce a constructor.

Define the interface and create an abstract base class that provides the most likely implementation of the constructor -- probably just saving the resource context passed in.

Encourage, but don't require, plugin authors to derive from the base class. There may be other useful methods that the base class could also provide.

Continue to use reflection to check the plugins.

Stephen P
  • 14,422
  • 2
  • 43
  • 67
0

As others have alluded to, using an abstract class to take care of the plumbing details is a common pattern for what you're trying to accomplish. Here is one design that avoids the need for a constructor with special parameters if the consumer inherits from the abstract base class Plugin:

public interface IPlugin
{
    void Initialize(IResourceContext context);    

    //Other methods...
}

public abstract class Plugin : IPlugin
{
    protected IResourceContext Context { get; private set; }

    void IPlugin.Initialize(IResourceContext context)
    {
        Context = context;
    }

    //Abstract declaration of other methods...
}

Your code has to call Initialize behind the scenes after creating the Plugin, but this detail is hidden from typical users, as they generally don't have to implement IPlugin directly. Your typical user can just define a Plugin descendant and work with the Context property.

You might also want to look into various dependency injection frameworks (such as Ninject), though they're probably overkill for what you're doing. Still, looking at how they work may give you some ideas on different ways dependency injection can be managed.

Dan Bryant
  • 27,329
  • 4
  • 56
  • 102
  • I just wrote a comment on another answer, in which I suggested that exact interface you wrote there, but I hadn't yet seen your answer. That's pretty much it. If you have complicated common functions you can do it in a base class, and initialization can be done with an interface enforced method. No need for a second factory type, as the other answer suggested. – Matheus Rocha Jun 17 '23 at 03:37