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





Я так не думаю.
Вы также не можете использовать для этого абстрактный класс.
Не буду слишком резким, но вы неправильно поняли назначение интерфейсов.
Интерфейс означает, что несколько человек могут реализовать его в своих классах, а затем передавать экземпляры этих классов другим классам для использования. Созидание создает ненужную сильную связь.
Похоже, вам действительно нужна какая-то система регистрации, чтобы люди регистрировали экземпляры используемых классов, реализующих интерфейс, или фабрик, которые могут создавать указанные элементы по запросу.
Хуан,
К сожалению, на строго типизированном языке это невозможно обойти. Вы не сможете гарантировать во время компиляции, что классы смогут быть созданы с помощью вашего кода на основе активатора.
(ред: удалено ошибочное альтернативное решение)
Причина в том, что, к сожалению, невозможно использовать интерфейсы, абстрактные классы или виртуальные методы в сочетании с конструкторами или статическими методами. Короткая причина в том, что первые не содержат явной информации о типе, а вторые требуют явной информации о типе.
Конструкторы и статические методы должен имеют явную (прямо в коде) информацию о типе, доступную во время вызова. Это необходимо, потому что нет экземпляра задействованного класса, который может быть запрошен средой выполнения для получения базового типа, который требуется среде выполнения, чтобы определить, какой фактический конкретный метод вызывать.
Вся суть интерфейса, абстрактного класса или виртуального метода состоит в том, чтобы иметь возможность сделать вызов функции без явной информацией о типе, и это возможно благодаря тому факту, что существует ссылка на экземпляр, который не имеет информации о «скрытом» типе. непосредственно доступен телефонный код. Итак, эти два механизма просто исключают друг друга. Их нельзя использовать вместе, потому что, когда вы их смешиваете, вы вообще нигде не получаете никакой конкретной информации о типе, а это означает, что среда выполнения не знает, где найти функцию, которую вы просите ее вызвать.
Напоминаю всем, что:
Написание инструмента для захвата всех конкретных классов, реализующих определенный интерфейс / имеющих атрибут, и проверка наличия у него конструктора без параметров занимает около 5 минут усилий по кодированию. Вы добавляете его на этап после сборки, и теперь у вас есть структура для любого другого статического анализа, который вам нужно выполнить.
Язык, компилятор, IDE, ваш мозг - все это инструменты. Используй их!
Вы можете использовать ограничение параметра типа
interface ITest<T> where T: new()
{
//...
}
class Test: ITest<Test>
{
//...
}
Нет, ты не можешь этого сделать. Может быть, в твоей ситуации был бы полезен заводской интерфейс? Что-то вроде:
interface FooFactory {
Foo createInstance();
}
Для каждой реализации Foo вы создаете экземпляр FooFactory, который знает, как его создать.
Вам не нужен конструктор без параметров для Activator для создания экземпляра вашего класса. Вы можете иметь параметризованный конструктор и передавать все параметры из Activator. Проверьте MSDN на этом.
that's one of the reasons I don't understand why it cannot be a part of the contract in the interface
Это косвенный механизм. Общий позволяет «обмануть» и отправлять информацию о типе вместе с интерфейсом. Здесь важно помнить, что ограничение не на интерфейс, с которым вы работаете напрямую. Это ограничение не для самого интерфейса, а для какого-то другого типа, который будет «перемещаться» по интерфейсу. Боюсь, это лучшее объяснение, которое я могу предложить.
В качестве иллюстрации этого факта я укажу на дыру, которую я заметил в коде aku. Можно написать класс, который бы компилировался нормально, но не работал во время выполнения, когда вы пытались создать его экземпляр:
public class Something : ITest<String>
{
private Something() { }
}
Что-то происходит от ITest
Чтобы предотвратить появление немного этой проблемы, вам необходимо добавить еще одно ограничение к T, как показано ниже:
public interface ITest<T>
where T : ITest<T>, new()
{
}
Обратите внимание на новое ограничение: T: ITest
Даже в этом случае это не предотвратит все случаев дыры. Приведенный ниже код будет компилироваться нормально, потому что A имеет конструктор без параметров. Но поскольку конструктор B без параметров является частным, создание экземпляра B с вашим процессом завершится ошибкой во время выполнения.
public class A : ITest<A>
{
}
public class B : ITest<A>
{
private B() { }
}
Вызовите метод RegisterType с типом и ограничьте его с помощью универсальных шаблонов. Затем вместо того, чтобы ходить по сборкам в поисках разработчиков ITest, просто сохраните их и создайте оттуда.
void RegisterType<T>() where T:ITest, new() {
}
Итак, вам нужен вещь, который может создавать экземпляры неизвестного типа, реализующие интерфейс. У вас есть три основных варианта: объект фабрики, объект Type или делегат. Вот данные:
public interface IInterface
{
void DoSomething();
}
public class Foo : IInterface
{
public void DoSomething() { /* whatever */ }
}
Использование Type довольно некрасиво, но имеет смысл в некоторых сценариях:
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 действительно имеет конструктор по умолчанию. Кроме того, отражение будет немного медленным, если это критичный для производительности код.
Наиболее распространенное решение - использовать фабрику:
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());
}
Преимущество здесь в том, что код короткий и простой, может работать с методом построения любой и (с замыканиями) позволяет легко передавать дополнительные данные, необходимые для создания объектов.