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

StackOverflow https://stackoverflow.com/questions/26903

Вопрос

Есть ли какой-нибудь способ?

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

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

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

Интерфейс будет внутренним по отношению к сборке

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

Это было полезно?

Решение

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

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

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

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

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

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

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

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

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

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

public class A : ITest<A>
{
}

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

Другие советы

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

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

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

Хуан,

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

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

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

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

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

Вы можете использовать ограничение параметра типа

interface ITest<T> where T: new()
{
    //...
}

class Test: ITest<Test>
{
    //...
}

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

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());
}

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

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

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

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

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

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

  1. Написать атрибуты в .NET очень просто
  2. Написать инструменты статического анализа в .NET, обеспечивающие соответствие стандартам компании, несложно

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

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

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

interface FooFactory {
    Foo createInstance();
}

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top