Почему C # не принимает требования конструктора с параметрами на обобщениях? - PullRequest
2 голосов
/ 15 августа 2011

Работая с C # Generics, у вас может быть такой класс:

class Foo<T> where T:new() {}

Это означает, что тип T должен иметь конструктор без параметров.Было бы неплохо, если бы у нас было:

class Foo<T> where T : new(string)
{
    private T CreateItem()
    {
        string s="";
        return new T(s);
    }
}

Есть ли причина, по которой Microsoft не добавила эту функцию в язык?

Ответы [ 3 ]

10 голосов
/ 15 августа 2011

Есть ли причина, по которой Microsoft не добавила эту функцию в язык?

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

void M<T>(T t) where T has an accessible method with signature double Foo(int)
{
    double x = t.Foo(123);
}

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

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

Функция, которую вы хотите, - это та же самая функция, только с видом метода, ограниченным конструктором.

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

7 голосов
/ 15 августа 2011

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

public interface IFactory<T>
{
   T CreateItem(string s);
}

class Foo<TFactory,T> where TFactory : IFactory<T>, new()
{
    private T CreateItem()
    {
        var factory = new TFactory();
        string s="";
        return factory.CreateItem(s);
    }
}

Используя этот шаблон, скажем, у вас есть класс Bar, который имеетконструктор, принимающий одну строку:

public class Bar
{
   public Bar(string laa)
   {}
}

Вам просто нужен BarFactory, который реализует IFactory<Bar>

public class BarFactory : IFactory<Bar>
{
   public BarFactory () {}
   public Bar CreateItem(string s)
   {
      return new Bar(s);
   }
}

Теперь вы можете использовать эту фабрику с Foo

var foo = new Foo<BarFactory,Bar>(); // calls to CreateItem() will construct a Bar
1 голос
/ 17 августа 2011

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

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

(Идеальное решение, позволяющее добавлять статические методы в интерфейс, было бы идеальным.)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...