2010-06-09 9 views
10

Ich habe eine .net-App, die einen Mechanismus zum Erweitern der App mit Plugins bietet. Jedes Plugin muss eine Plugin-Schnittstelle implementieren und darüber hinaus einen Konstruktor bereitstellen, der einen Parameter (einen Ressourcenkontext) erhält.Kann eine Schnittstelle die Signatur eines C# -Konstruktors definieren

Während der Instantiierung der Plugin-Klasse schaue ich über Reflektion, ob der benötigte Konstruktor existiert und falls ja, instantiiere ich die Klasse (via Reflection). Wenn der Konstruktor nicht existiert, werfe ich eine Ausnahme aus, die besagt, dass das Plugin nicht erstellt werden konnte, weil der gewünschte Konstruktor nicht verfügbar ist.

Meine Frage ist, ob es eine Möglichkeit gibt, die Signatur eines Konstruktors in der Plugin-Schnittstelle zu deklarieren, so dass jeder, der die Plugin-Schnittstelle implementiert, auch einen Konstruktor mit der gewünschten Signatur versehen muss. Dies würde die Erstellung von Plugins erleichtern.

Ich glaube nicht, dass eine solche Möglichkeit besteht, weil ich denke, ein solches Merkmal fällt nicht in den Hauptzweck für welche Schnittstellen für entworfen wurden, aber vielleicht kennt jemand eine Erklärung, dass dies der Fall ist, so etwas wie:

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

Ich möchte hinzufügen, dass ich den Konstruktor nicht als parameterless ändern und dann den Ressource-Kontext durch eine Eigenschaft festlegen, da dies die Erstellung von Plugins viel komplizierter macht. Die Personen, die Plugins schreiben, sind keine Personen mit tiefer Programmiererfahrung. Die Plugins werden verwendet, um statistische Daten zu berechnen, die von der App visualisiert werden.


Danke für alle Antworten.

Ich habe mich entschieden, dass es eine Schnittstelle sein soll, weil ich nicht möchte, dass die Plugin-Programmierer von einer abstrakten Klasse erben, so dass sie die Möglichkeit verliert, von einer eigenen Basisklasse zu erben . Darüber hinaus stellt das Ableiten von einer abstrakten Klasse nicht sicher, dass der Plugin-Programmierer den benötigten Konstruktor wirklich bereitstellt. Es macht es nur wahrscheinlicher (Der Programmierer hat immer noch die Möglichkeit, nur einen Konstruktor hinzuzufügen, der den gewünschten Parameter enthält, aber auch zusätzliche Parameter, und das ist auch schlecht. Siehe die Kommentare zur Antwort von Ken Browning).

Obwohl ich in meinem Beitrag erwähnte, dass ich solch eine Eigenschaft nicht will, habe ich die Antwort von Danny Varod als akzeptiert markiert, weil ich denke, in meiner Situation ist es die am besten geeignete Lösung. Danke an alle, die geantwortet haben.

Antwort

7

Plug-in extendability ist ein Favorit von mir ...

Was ich tue, ist sicher, dass das Plugin macht entweder implementiert die Schnittstelle oder erbt die Basisklasse des entsprechenden „Plugin-Buchse“.

An einigen Stellen sind Basisklassen geeigneter (wenn das Plug-in eine Art X ist),
in einigen Schnittstellen sind passender (wenn das Plug-in IX tut).

Ich übergebe den Kontext nicht an das Konstrukt, stattdessen verwende ich eine Eigenschaft für diesen und einen parameterlosen öffentlichen Konstruktor.

Dies ermöglicht auch eine einfachere Deserialisierung von Plug-Ins durch Reflexion.

+0

+1 für die Verwendung einer Eigenschaft. –

5

Nein, das existiert nicht. Sie suchen hier wahrscheinlich eine abstrakte Klasse.

7

Schnittstellen können Konstruktoren nicht deklarieren. Sie könnten stattdessen eine abstrakte Klasse verwenden.

+0

Abstrakte Klassen können die Erstellung von Konstruktoren in ihren Unterklassen entweder nicht erzwingen, aber können sie? –

+0

Sie können, wenn Sie ein ctor mit Parametern definieren, die die automatische Erstellung eines parameterlosen ctor deaktiviert. Dadurch erzwingst du eine Kettenverkettung. Sie können einfach nicht von einer Klasse ableiten, ohne einen Basisvektor aufzurufen, es sei denn, die Basisklasse hat einen parameterlosen ctor. – Femaref

+0

@Matthew Scharley, eine Subklasse muss immer den Konstruktor in einem Parent "aufrufen". Wenn also das Parent keinen parameterlosen Konstruktor hat, muss das Kind irgendwie die Parameter dazu im Konstruktor bekommen. Dies kann durch Erzwingen des gleichen Konstruktors auf dem Kind sein oder auch nicht, aber es ist eine Möglichkeit sicherzustellen, dass die von dem Elternteil benötigten Informationen – johnc

1

Leider können Schnittstellen in C# nur Methoden, Eigenschaften, Ereignisse oder Indexer enthalten.

Sie könnten eine abstrakte Klasse verwenden, von der alle Plugins erben würden. Sie könnten sie zwingen, in diesem Fall die Konstruktorsignatur zu implementieren.

1

Die Schnittstelle kann keinen Konstruktor deklarieren/erzwingen.

Definieren Sie die Schnittstelle und eine abstrakte Basisklasse erstellen, die höchstwahrscheinliche Umsetzung des Konstruktor bietet -. Wahrscheinlich nur den Ressourcenkontext übergab Speicher

Ermutigen, aber nicht erfordern, Autoren Plugin abgeleitet von der Basisklasse.Es kann andere nützliche Methoden geben, die die Basisklasse ebenfalls bereitstellen könnte.

Setzen Sie fort, Reflexion zu verwenden, um die Plugins zu überprüfen.

0

Wie andere bereits erwähnt haben, ist die Verwendung einer abstrakten Klasse, die sich um die Sanitärdetails kümmert, ein übliches Muster für das, was Sie erreichen möchten. Hier ist ein Design, das die Notwendigkeit für einen Konstruktor mit speziellen Parametern vermeidet, wenn der Verbraucher erbt von der abstrakten Basisklasse 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... 
} 

Ihr Code hat initialisieren hinter den Kulissen zu rufen, nachdem das Plugin erstellen, aber dieses Detail ist Für typische Benutzer unsichtbar, da sie IPlugin in der Regel nicht direkt implementieren müssen. Ihr typischer Benutzer kann einfach einen Plugin-Nachkommen definieren und mit der Context-Eigenschaft arbeiten.

Sie könnten auch in verschiedene Abhängigkeitsinjektion Frameworks (wie Ninject) suchen, obwohl sie wahrscheinlich übertrieben sind für das, was Sie tun. Dennoch können Sie, wenn Sie sich ansehen, wie sie funktionieren, einige Ideen dazu geben, wie die Abhängigkeitsinjektion gehandhabt werden kann.

1

Alternativ können Sie versuchen, eine Fabrik mit: der Konstruktor Unterschrift eine Methode Unterschrift eines anderen Typs machen:

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

und dann so etwas wie (und ich immer vermasseln dieser Teil, wenn ich will es kurz sein (daher die Bearbeitung):

public class PluginContainer 
{ 
    public IPlugin LoadPlugin<T>(IResourceContext context) where T: PluginFactory, new() 
    { 
     var factory = new T(); 
     return factory.Create(context); 
    } 
} 
Verwandte Themen