Больше мелочей, чем действительно важно: почему нет ограничения new() на Activator.CreateInstance‹T›()?

Я думаю, что есть люди, которые могут ответить на этот вопрос, это вопрос из любопытства:

Универсальный метод CreateInstance из System.Activator, представленный в .NET v2, не имеет ограничений типа для универсального аргумента, но требует конструктора по умолчанию для активированного типа, иначе выдается MissingMethodException. Мне кажется очевидным, что этот метод должен иметь ограничение типа, например

Activator.CreateInstance<T>() where T : new() {
   ...
}

Просто упущение или какой-то анекдот здесь скрывается?

Обновить

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

private T Create<T>() where T : struct, new()
error CS0451: The 'new()' constraint cannot be used with the 'struct' constraint

Тем не менее, см. комментарии, структура может использоваться в качестве аргумента типа для универсального метода, определяющего ограничение new(). В этом случае данный ответ кажется единственной веской причиной не ограничивать метод...

Спасибо, что посмотрели это!


person flq    schedule 03.03.2011    source источник
comment
+1 Очень интересный вопрос. Я не могу придумать причину, по которой его там не должно быть, поэтому мне тоже любопытно узнать, почему!   -  person Adam Robinson    schedule 04.03.2011
comment
Что касается вашего обновления, потому что оно избыточно; все типы значений имеют конструктор по умолчанию, который инициализирует все значения значениями по умолчанию (это то, что вы получаете, когда вызываете default(T).   -  person Adam Robinson    schedule 04.03.2011
comment
Все структуры имеют ctors по умолчанию в C#.   -  person Linkgoron    schedule 04.03.2011
comment
Просто чтобы уточнить, учитывая, что void Foo<T>() where T:new() {}, Foo<DateTime>(); или Foo<int>(); оба являются вполне допустимыми вызовами, на которые компилятор не будет жаловаться.   -  person Adam Robinson    schedule 04.03.2011
comment
Отличный вопрос. Я сбит с толку. Ограничение new, по-видимому, было введено одновременно с дженериками, так что это не похоже на проблему обратной совместимости. Может они просто забыли?   -  person StriplingWarrior    schedule 04.03.2011


Ответы (1)


Я могу ошибаться, но главное преимущество, как я вижу, заключается в том, что он позволяет вам сделать что-то вроде этого:

// Simple illustration only, not claiming this is awesome code!
class Cache<T>
{
    private T _instance;

    public T Get()
    {
        if (_instance == null)
        {
            _instance = Create();
        }

        return _instance;
    }

    protected virtual T Create()
    {
        return Activator.CreateInstance<T>();
    }
}

Обратите внимание, что если бы Activator.CreateInstance<T> имел ограничение where T : new(), то вышеприведенному классу Cache<T> также потребовалось бы это ограничение, которое было бы чрезмерно ограничительным, поскольку Create является виртуальным методом, и некоторые производные классы могут захотеть использовать другие средства ограничения. создание экземпляра, например вызов внутреннего конструктора типа или использование статического метода построителя.

person Dan Tao    schedule 03.03.2011
comment
+1. Это отличный момент. Хотя вы принимаете на себя некоторый риск (возможно, что T здесь не имеет конструктора по умолчанию, очевидно), это кажется вполне допустимым случаем. - person Adam Robinson; 04.03.2011
comment
Кстати, похоже, что System.Runtime.CompilerServices.ConditionalWeakTable делает именно это. - person Adam Robinson; 04.03.2011
comment
Также стоит отметить, что метод CreateInstance<T>, ограниченный where T : new(), будет полностью избыточным. Поскольку T должно быть известно во время компиляции, любой T, ограниченный where T : new(), может быть создан с использованием синтаксиса var x = new T(). - person LukeH; 04.03.2011
comment
@LukeH: Ха, да, довольно забавно думать, что это вопрос, который задают, тогда как если бы Activator.CreateInstance<T> действительно имело ограничение where T : new(), наверняка возник бы ТАКОЙ вопрос: что, черт возьми, такое? точка Activator.CreateInstance<T>? - person Dan Tao; 04.03.2011
comment
@Dan, LukeH: Как бы то ни было, звонки new T() фактически превращаются в звонки Activator.CreateInstance<T>(). - person Adam Robinson; 04.03.2011
comment
@Adam: Это верно для типов ссылок, но, насколько мне известно, new T() всегда будет компилироваться в инструкцию initobj для типов значений. (Я не уверен, почему new T() нельзя скомпилировать в инструкцию newobj для ref-типов, а не в вызов CreateInstance<T>.) - person LukeH; 04.03.2011
comment
@Adam: Если я объявлю метод T M<T>() where T : new() { return new T(); }, то путь типа значения имеет особый регистр с использованием initobj, компилируя его в нечто, примерно эквивалентное if (default(T) != null) return default(T); return Activator.CreateInstance<T>(); - person LukeH; 04.03.2011