Почему общее ограничение new () не удовлетворяется классом с необязательными параметрами в конструкторе? - PullRequest
5 голосов
/ 30 апреля 2010

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

public class Factory<T> where T : new()
{
    public T Build()
    {
        return new T();
    }
}

public class Widget
{
    public Widget(string name = "foo")
    {
        Name = name;
    }

    public string Name { get; set; }
}

public class Program
{
    public static void Main()
    {
        var widget = new Widget(); // this is valid
        var factory = new Factory<Widget>(); // compiler error
    }
}

1 Ответ

7 голосов
/ 30 апреля 2010

Хотя это по логике должно работать, к сожалению, нет. CLR по-прежнему видит ваш конструктор как конструктор на основе параметров.

Помните, что, хотя C # поддерживает необязательные параметры, это делается на уровне компилятора во время компиляции. Базовый тип все еще содержит конструктор, принимающий единственный параметр. Что касается CLR, «параметры по умолчанию» преобразуются в атрибуты, например:

public Widget(([Optional, DefaultParameterValue("foo")] string name) { // ...

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


Edit:

В ответ на ваш комментарий:

Чего я не понимаю, так это того, почему компилятор C # не может сгенерировать необходимый код для удовлетворения CLR

Теоретически, команда компилятора C # может заставить язык генерировать два отдельных конструктора вместо одного конструктора, помеченного атрибутами. Это может привести к взрыву во многих конструкторах, так как именованные параметры создают возможности для многих, многих возможных комбинаций «конструкторов» (или вызовов методов для методов), особенно когда доступно несколько аргументов. Лично я рад, что это не так, поскольку это может вызвать путаницу из-за переизбытка методов и конструкторов в сгенерированных типах, что приведет к тому, что общедоступный API будет сильно отличаться от кода, который его сгенерировал. Возьмите следующий конструктор:

public Widget(
          int id = 0, 
          string name = "foo", 
          float width=1.0f, 
          float height=1.0f, 
          float depth=1.0f
       ) { // ... 

Если бы вы автоматически генерировали здесь все возможные комбинации, компилятору потребовалось бы сгенерировать конструкторы 120 для этого единственного конструктора, поскольку существует N! возможные способы назвать это ...

...