Почему в C # есть новое ограничение (), но нет другого подобного ограничения?

19

В обобщениях C # мы можем объявить ограничение для параметра типа, Tимеющего конструктор по умолчанию, например where T : new(). Тем не менее, никакие другие виды ограничений, как это не действует, new(string)например, и т. Д.

Что является причиной этого с точки зрения языкового дизайна и / или реализации?

Есть ли что-то в том, как работают конструкторы, или в том, как реализована система типов, которая запрещает это (или, по крайней мере, усложняет)? Если так, то, что это? Я помню чтение где-то, что на default(T)самом деле компилируется new T()для T : struct. Возможно, это связано с этим?

Или это просто дизайнерское решение, принятое во избежание усложнения языка?

Теодорос Чатзигианнакис
источник
new(string)не является ограничением конструктора по умолчанию. Ваш вопрос равносилен высказыванию "Почему нет ограничений, которые требуют определенных подписей конструктора?" Очень вероятно, потому что такое ограничение не будет особенно полезным.
Роберт Харви
3
@RobertHarvey Да, это именно то, что я говорю. По сути, я спрашиваю, есть ли что-то, что делает конструкторы по умолчанию специальными для реализации, или это был просто произвольный выбор включить это, а не другое.
Теодорос Чатзигианнакис,
Конструкторы по умолчанию полезны определенными и важными способами. Например, они делают тип легко сериализуемым.
Роберт Харви
6
Конкретные сигнатуры ctor могут быть полезны. Например, если ваша переменная типа ограничена коллекцией <T> с ctor of T (Collection <T>), вы знаете, что вы можете создавать новые коллекции, используя другую. Стоит ли эта полезность дополнительной сложности, однако, вопрос.
Фоши

Ответы:

5

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

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

...

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

Крис Хэннон
источник
2
Эта цитата, вырванная из контекста, очень вводит в заблуждение. Это говорит о том, что Эрик думал, что Microsoft должна была «пройти весь путь», что вовсе не является его позицией. Он, на самом деле, категорически против предложенной функции.
Роберт Харви
1
Спасибо, это частично отвечает на мой вопрос: ближе к концу своего ответа Эрик Липперт упоминает, что это ограничение IL, и включение этой функции потребует дополнения к IL. Было бы идеально , если бы вы могли предоставить источник о том , что генерируется IL для той части , которая будет реализована - то есть, в общем , вызов конструктора по умолчанию.
Теодорос Чатзигианнакис,
@TheodorosChatzigiannakis: Почему бы вам не запустить декомпилятор Telerik и не выяснить это для себя?
Роберт Харви
2
@RobertHarvey Я не думаю, что это вообще говорит о его позиции, но я добавил дополнительную цитату, чтобы подчеркнуть его позицию.
Крис Хэннон
1
Хм, F # имеет более продвинутые способы ограничения типа , такие как проверка во время компиляции, если у класса есть оператор. Возможно, F # нуждается в более мощной системе ограничений, чем C #, из-за очень строгой проверки типов. Тем не менее, этот язык может реализовывать расширенные способы поддержки классов в .Net Framework.
OnesimusUnbound
16

Декомпиляция (в соответствии с предложением Роберта Харви) дала следующее всем, кто заинтересовался. Этот метод:

static T GenericMake<T>()
    where T : new()
{
    return new T();
}

Видимо, при компиляции становится так:

private static T GenericMake<T>()
    where T : new()
{
    T t;
    T t1 = default(T);
    if (t1 == null)
    {
        t = Activator.CreateInstance<T>();
    }
    else
    {
        t1 = default(T);
        t = t1;
    }
    return t;
}
  • Если Tэто тип значения, new()становится default(T).
  • Если Tэто ссылочный тип, new()работает с использованием отражения. Activator.CreateInstance()внутренне звонки RuntimeType.CreateInstanceDefaultCtor().

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

Теодорос Чатзигианнакис
источник
4
Интересный. Зачем дублировать вызов default(T) для типов значений?
Авнер Шахар-Каштан
2
@ AvnerShahar-Kashtan Я не знаю. Это может быть артефакт процесса компиляции / декомпиляции, например, tпеременная (которая может быть заменена вложенными returnоператорами).
Теодорос Чатзигианнакис,