Почему Activator.CreateInstance <T>() разрешен без ограничения общего типа() общего типа?

В приведенном ниже примере кода метод CompileError не будет компилироваться, поскольку для него требуется ограничение where T : new(), как показано в методе CreateWithNew(). Однако метод CreateWithActivator<T>() компилируется просто отлично без ограничения.

public class GenericTests
{
    public T CompileError<T>() // compile error CS0304
    {
        return new T();
    }

    public T CreateWithNew<T>() where T : new() // builds ok
    {
        return new T();
    }

    public T CreateWithActivator<T>() // builds ok
    {
        return Activator.CreateInstance<T>();
    }
}

Почему это?

Согласно qaru.site/info/204559/..., который ссылается на документацию MSDN и этот вопрос, выражение new T() в дженериках фактически реализовано с помощью Activator.CreateInstance<T>(). Поэтому я не понимаю, почему вызов new T() требует, чтобы общий тип был ограничен таким образом, который можно опустить при использовании Activator.CreateInstance<T>().

Или, чтобы поставить вопрос наоборот: какая точка ограничения where T : new(), если легко создать экземпляры T в универсальном методе без ограничения, напрямую используя ту же самую базовую инфраструктуру

Ответ 1

Существует концептуальная разница между Activator и T():

  • Activator.CreateInstance<T> - Я хочу создать новый экземпляр T с помощью его конструктора по умолчанию - И выбросить Exception, если он не имеет ( Поскольку произошло что-то очень плохое, и я хочу обработать его/бросить сам).

    • Боковое примечание: имейте в виду, что как MSDN говорит:

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

      так как обычно вы хотели бы использовать конструктор, когда Type известен во время компиляции (CreateInstance<T>()использует RuntimeTypeHandle.CreateInstance, который медленнее [Также, почему Activator.CreateInstance<T> сам не нуждается в ограничении]).

  • T() - Я хочу вызвать пустой конструктор T, предположительно, как стандартный вызов конструктора.

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

Более того; Вы должны предпочитать ошибки времени компиляции над Exceptions, если возможно.

Тот факт, что T() реализуется внутренне с использованием отражения, не имеющего отношения к среднему случаю "Я просто хочу экземпляр по умолчанию T" (конечно, внутренняя реализация важна, если вы заботитесь о производительности/и т.д...)суб > .

Ответ 2

Это просто сахар. Самок самоконтроля, если можно так выразиться. Например, вы можете вызвать отражение почти любого метода в любом типе (в некоторых случаях даже без примера!), Но это не так, не согласны ли вы? В какой-то момент ваш код будет просто незаметным, и во время выполнения многие ошибки будут всплывать, это очень плохо. Итак, если вы можете контролировать себя перед выполнением - просто сделайте это.

Ограничение поможет вам понять это во время компиляции.

Ответ 3

Метод Activator.CreateInstance<T>() подвергается коду пользователя, чтобы позволить возможность использования универсального класса по-разному, некоторые из которых потребуют, чтобы параметр типа удовлетворял определенным ограничениям, а некоторые из них - нет. Например, класс Foo<T> может поддерживать любой из следующих шаблонов использования:

  • Клиентский код предоставляет функцию, которая возвращает новый T.

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

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

Шаблоны №1 и №3 должны использоваться с любыми T, а # 2 должны работать только с типами беззазорных конструкторов. Имея Activator.CreateInstance<T>() для компиляции для неограниченного T и работа для типов T, у которых есть конструкторы без параметров, упрощает поддержку кода для всех трех шаблонов использования. Если Activator.CreateInstance<T> имеет ограничение new, было бы очень неудобно использовать его с параметрами общего типа, у которых их нет.