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

Стандарт JLS

JLS 7 15.17.2. Оператор отдела / говорит:

Целые деления округляются до 0.

blockquote>

Вот почему 1/2 не дает поплавок.

Преобразование только одного для плавания, как в (float)1/2, достаточно, потому что 15.17. Мультипликативные операторы говорят:

Бинарное числовое продвижение выполняется по операндам

blockquote>

и 5.6.2. Двоичное числовое продвижение говорит:

  • Если любой из операндов имеет тип double, другой преобразуется в double.
  • В противном случае, если любой из операндов типа float, другой преобразуется в float
blockquote>

10
задан juan 24 September 2008 в 15:52
поделиться

10 ответов

Juan Manuel сказал:

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

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

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

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

Что-то происходит из ITest <T>, но не реализует конструктора без параметров. Это скомпилирует прекрасный, потому что Строка действительно реализует конструктора без параметров. Снова, ограничение находится на T, и поэтому Строке, а не ITest или Чем-то. Так как ограничение на 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() { }
}
5
ответ дан 3 December 2019 в 13:47
поделиться

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

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

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

21
ответ дан 3 December 2019 в 13:47
поделиться

Juan,

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

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

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

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

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

5
ответ дан 3 December 2019 в 13:47
поделиться

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

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

class Test: ITest<Test>
{
    //...
}
5
ответ дан 3 December 2019 в 13:47
поделиться

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

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

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 - то, что действительно имеет значение. Фабрика является просто классом удобства для классов, которые действительно предоставляют конструктора по умолчанию. Это - самое простое и часто лучшее решение.

Третье currently-uncommon-but-likely-to-become-more-common решение использует делегата:

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

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

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

4
ответ дан 3 December 2019 в 13:47
поделиться

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

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

0
ответ дан 3 December 2019 в 13:47
поделиться

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

void RegisterType<T>() where T:ITest, new() {
}
1
ответ дан 3 December 2019 в 13:47
поделиться

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

interface FooFactory {
    Foo createInstance();
}

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

0
ответ дан 3 December 2019 в 13:47
поделиться

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

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

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

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

0
ответ дан 3 December 2019 в 13:47
поделиться

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

0
ответ дан 3 December 2019 в 13:47
поделиться
Другие вопросы по тегам:

Похожие вопросы: