Может ли интерфейс определить подпись С# -структора

У меня есть .net-приложение, которое предоставляет механизм для расширения приложения с помощью плагинов. Каждый плагин должен реализовать плагин-интерфейс и должен предоставить дополнительно конструктор, который получает один параметр (контекст ресурса).

Во время создания класса плагина я просматриваю через отражение, если существует необходимый конструктор, и если да, я создаю экземпляр класса (через Reflection). Если конструктор не существует, я выдаю исключение, в котором говорится, что плагин не может быть создан, потому что желаемый конструктор недоступен.

Мой вопрос в том, есть ли способ объявить подпись конструктора в интерфейсе плагина, чтобы каждый, кто реализует плагин-интерфейс, должен также предоставить конструктор с нужной сигнатурой. Это облегчило бы создание плагинов.

Я не думаю, что такая возможность существует, потому что я считаю, что такая функция не входит в основную цель для того, для каких интерфейсов были разработаны, но, возможно, кто-то знает выражение, которое делает это, например:

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

Я хочу добавить, что я не хочу изменять конструктор без параметров и затем устанавливать ресурс-контекст через свойство, потому что это сделает создание плагинов намного более сложным. Лица, которые пишут плагины, не являются людьми с глубоким опытом программирования. Плагины используются для расчета статистических данных, которые будут визуализироваться приложением.


Спасибо за все ответы.

Ive решил, что я позволю ему быть интерфейсом, потому что я не хочу заставить программистов-плагинов наследовать от абстрактного класса, чтобы он или она терял возможность наследовать из собственного базового класса. Кроме того, вывод из абстрактного класса не гарантирует, что программист плагина действительно предоставляет необходимый конструктор. Это делает его еще более вероятным (у программиста все еще есть возможность добавить только один конструктор, который содержит желаемый параметр, но также имеет дополнительные параметры, и это также плохо. См. Комментарии к ответу Кена Браунинга).

Хотя я упомянул в своем посте, что я не хочу такого имущества, я отметил ответ Дэнни Варода как принятый, потому что я считаю в своей ситуации его наиболее подходящее решение. Спасибо всем, кто ответил.

Ответ 1

Расширяемость подключаемого модуля является моей любимой...

Что я делаю, убедитесь, что подключаемый модуль либо реализует интерфейс, либо наследует базовый класс соответствующего "plugin socket".

В некоторых местах базовые классы более подходят (если плагин - это своего рода X),
в некоторых интерфейсах более уместны (если подключаемый модуль IX).

Я не передаю контекст конструкции, вместо этого я использую свойство для этого и беззазорный публичный конструктор.

Это также облегчает десериализацию подключаемых модулей с использованием отражения.

Ответ 2

Интерфейсы не могут объявлять конструкторы. Вместо этого вы можете использовать абстрактный класс.

Ответ 3

Нет, этого не существует. Вероятно, вы ищете абстрактный класс.

Ответ 4

К сожалению, интерфейсы на С# могут содержать только методы, свойства, события или индексы.

Вы можете использовать и абстрактный класс, на который наследуются все плагины. Вы могли бы заставить их реализовать подпись конструктора в этом случае.

Ответ 5

Интерфейс не может объявить/применить конструктор.

Определите интерфейс и создайте абстрактный базовый класс, который обеспечивает наиболее вероятную реализацию конструктора - возможно, просто сохранит переданный контекст ресурса.

Поощряйте, но не требуйте, чтобы авторы плагинов извлекались из базового класса. Могут быть и другие полезные методы, которые также может предоставить базовый класс.

Продолжайте использовать отражение, чтобы проверить плагины.

Ответ 6

В качестве альтернативы вы можете попробовать использовать factory: сделать подпись конструктора сигнатурой метода другого типа:

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

а затем что-то вроде (и я всегда испортил эту часть, если я хочу, чтобы она была короткой, следовательно, редактирование):

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

Ответ 7

Как указывали другие, использование абстрактного класса для ухода за деталями сантехники является общей моделью того, что вы пытаетесь выполнить. Вот один дизайн, который позволяет избежать необходимости в конструкторе со специальными параметрами, если потребитель наследует от абстрактного базового класса 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...
}

Ваш код должен вызывать инициализацию за кулисами после создания плагина, но эта деталь скрыта от обычных пользователей, так как обычно им не нужно напрямую реализовывать IPlugin. Ваш типичный пользователь может просто определить потомка плагина и работать с свойством Context.

Возможно, вы также захотите изучить различные рамки инъекций зависимостей (такие как Ninject), хотя они, вероятно, слишком переполнены тем, что ты делаешь. Тем не менее, глядя на то, как они работают, вы можете дать несколько идей по различным способам управления инъекциями зависимостей.