Как вы можете потребовать конструктор без параметров для типов, реализующих интерфейс?

Есть ли способ?

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

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

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

Интерфейс будет внутренним для сборки

Если у вас есть предложение для этого сценария без интерфейсов, я с радостью буду принимать это во внимание...

Ответ 1

Хуан Мануэль сказал:

что одна из причин, почему я не понимаю, почему она не может быть частью контракта в интерфейсе

Это косвенный механизм. Общий позволяет вам "обманывать" и отправлять информацию о типе вместе с интерфейсом. Здесь важно помнить, что ограничение связано не с интерфейсом, с которым вы работаете напрямую. Это не является ограничением для самого интерфейса, а на каком-то другом типе, который будет "проезжать" по интерфейсу. Это лучшее объяснение, которое я могу предложить, боюсь.

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

public class Something : ITest<String>
{
  private Something() { }
}

Что-то происходит от ITest <T> , но не реализует конструктор без параметров. Он будет компилироваться отлично, потому что String реализует конструктор без параметров. Опять же, ограничение находится на T и, следовательно, на String, а не на ITest или Something. Поскольку ограничение на T выполняется, это будет скомпилировано. Но он будет терпеть неудачу во время выполнения.

Чтобы предотвратить некоторые экземпляры этой проблемы, вам нужно добавить еще одно ограничение в T, как показано ниже:

public interface ITest<T>
  where T : ITest<T>, new()
{
}

Обратите внимание на новое ограничение: T: ITest <T> . Это ограничение указывает, что то, что вы передаете в параметр аргумента ITest <T> должен также выводить из ITest <T> .

Даже это не помешает всем. Приведенный ниже код будет скомпенсирован, поскольку A имеет конструктор без параметров. Но так как конструктор без параметров B является частным, создание экземпляра B с вашим процессом завершится неудачно во время выполнения.

public class A : ITest<A>
{
}

public class B : ITest<A>
{
  private B() { }
}

Ответ 2

Не слишком грубый, но вы неправильно поняли цель интерфейсов.

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

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

Ответ 3

Хуан,

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

(ed: удалено ошибочное альтернативное решение)

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

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

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

Ответ 5

Итак, вам нужна вещь, которая может создавать экземпляры неизвестного типа, который реализует интерфейс. У вас есть в основном три варианта: объект factory, объект Type или делегат. Здесь givens:

public interface IInterface
{
    void DoSomething();
}

public class Foo : IInterface
{
    public void DoSomething() { /* whatever */ }
}

Использование типа довольно уродливо, но имеет смысл в некоторых сценариях:

public IInterface CreateUsingType(Type thingThatCreates)
{
    ConstructorInfo constructor = thingThatCreates.GetConstructor(Type.EmptyTypes);
    return (IInterface)constructor.Invoke(new object[0]);
}

public void Test()
{
    IInterface thing = CreateUsingType(typeof(Foo));
}

Самая большая проблема с этим заключается в том, что во время компиляции у вас нет гарантии, что Foo фактически имеет конструктор по умолчанию. Кроме того, отражение немного медленное, если это будет критический код производительности.

Наиболее распространенным решением является использование factory:

public interface IFactory
{
    IInterface Create();
}

public class Factory<T> where T : IInterface, new()
{
    public IInterface Create() { return new T(); }
}

public IInterface CreateUsingFactory(IFactory factory)
{
    return factory.Create();
}

public void Test()
{
    IInterface thing = CreateUsingFactory(new Factory<Foo>());
}

В приведенном выше, IFactory - это действительно важно. factory - это просто класс удобства для классов, которые предоставляют стандартный конструктор. Это самое простое и часто лучшее решение.

Третье в настоящее время необычное, но вероятное, чтобы стать более распространенным решением, использует делегат:

public IInterface CreateUsingDelegate(Func<IInterface> createCallback)
{
    return createCallback();
}

public void Test()
{
    IInterface thing = CreateUsingDelegate(() => new Foo());
}

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

Ответ 6

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

void RegisterType<T>() where T:ITest, new() {
}

Ответ 7

Я так не думаю.

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

Ответ 8

Я хотел бы напомнить всем, что:

  • Написание атрибутов в .NET легко
  • Написание инструментов статического анализа в .NET, обеспечивающих соответствие стандартам компании, легко.

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

Язык, компилятор, IDE, ваш мозг - все это инструменты. Используйте их!

Ответ 9

Нет, вы не можете этого сделать. Может быть, для вашей ситуации был бы полезен интерфейс factory? Что-то вроде:

interface FooFactory {
    Foo createInstance();
}

Для каждой реализации Foo вы создаете экземпляр FooFactory, который знает, как его создать.

Ответ 10

Вам не нужен конструктор без параметров для активатора для создания экземпляра вашего класса. Вы можете иметь параметризованный конструктор и передать все параметры из активатора. Проверьте MSDN на этом.