Подтвердить что ты не робот

Дженерики не могут вывести второй параметр?

Я заметил, что компилятор С# не выводит второй общий параметр.
Пример:

Код шаблона С++: (да, я знаю, что шаблоны не работают как generics)

class Test {
public:
template <class T,class V> 
    T test(V v) {
       //do something with v
       return T();
    }
};

int i = 0;
Test t = new Test();
double j = t.test<double>(i); //infers V as int

Шаблоны (и generics) не могут вывести тип возвращаемого значения, поэтому в С++ я даю ему первый параметр шаблона, а второй параметр шаблона выводится из типа переменной.

Теперь, тот же пример в С#:

class Test {
    public T test<T,V>(V v) where T: new() {
       //do something with v
       return new T();
    }
};

int i = 0;
Test t = new Test();
double j = t.test<double>(i); //Error Using the generic method 'Test.test<T,V>(V)' requires '2' type arguments

Но если я использую 1 тип, мне не нужно явно указывать тип:

class Test {
    public V test<V>(V v) where V: new() {
       return new V();
    }
};

int i = 0;
Test t = new Test();
int j = t.test(i); //OK infers V as int.

Итак, почему не может использовать С# generics, выводящий второй тип (в то время как в шаблонах С++ это явно может)?
Я уверен, что он был разработан таким образом (я сомневаюсь, что команда .Net упустила это из виду), поэтому почему он сконструирован таким образом, что я должен явно указать оба типа?

Edit:

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

Так снова, почему создан С# таким образом? Чем отличается реализация языка, которая не позволяет явно объявить только один параметр?

4b9b3361

Ответ 1

С# был спроектирован как слегка более гибкий язык, чем С++.

В частности, я не думаю, что отличная идея сравнить С# generics с шаблонами С++ по разным причинам - они принципиально два очень разных подхода к решению подобных вещей в некоторых ситуациях. Подход С++, безусловно, является гибким в некоторых отношениях - хотя он не позволяет (как я понимаю) шаблоны, которые существуют только в двоичной форме, или создавать новые шаблоны во время выполнения. В принципе, подход к шаблонам С++ не подходит для остальных, как .NET подходит друг к другу.

Теперь о том, почему вы не можете указать некоторые аргументы типа и позволить другим быть выведенным (это решение языка, а не решение платформы, я уверен, что это будет возможно, насколько это касается самой .NET) - опять же, я считаю, что это ради простоты. Выбор точного правильного метода и правильных аргументов типа уже очень сложный в С# - более сложный, чем большинство разработчиков С# могут обходить головы. Он включает в себя:

  • Потенциально рассматривая методы для иерархии типов из типа времени компиляции целевого
  • Перегрузка по количеству параметров
  • Перегрузка по количеству параметров типа
  • Эффект именованных аргументов
  • Влияние дополнительных параметров
  • Влияние ограничений типа типового типа на типы параметров (не ограничения, заданные целевым методом, примечание)
  • Группа методов для делегирования конверсий
  • Анонимные преобразования функций
  • Вывод типа для аргументов типа
  • Динамическая типизация
  • Общая ковариация и контравариантность

Лично я считаю, что достаточно, чтобы окунуться в голову, не допуская еще больше возможностей с помощью "M все еще может быть кандидатом, если у него есть как минимум столько же параметров типа, сколько указано в аргументах типа". Вы также хотите, чтобы именованные типы аргументов и необязательные параметры типа?;)

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

Итак, да, это в основном ради простоты, а иногда и боли - но часто вы можете обойти это. Для каждой потенциальной функции вам необходимо учитывать:

  • Преимущество функции для разработчиков
  • Стоимость функции для конечных разработчиков с точки зрения времени, потраченного на ее понимание
  • Стоимость разработчикам языка при проектировании и конкретизации.
  • Стоимость для авторов компилятора при его правильном использовании
  • Стоимость тестовой команды при ее тщательном тестировании (в сочетании со всем остальным при перегрузке)
  • Стоимость будущих потенциальных возможностей (если это делает язык более сложным, что оставляет меньше "потенциально громоздкой" дополнительной сложности для других функций)

Ответ 2

Как сказал Дэн, С# не позволяет вам выводить только некоторые параметры типа общего набора параметров. Вероятно, это приведет к перегрузке в зависимости от количества общих параметров (что позволяет С#, по крайней мере для общих классов).

Однако вы можете указать параметры универсального класса и вывести параметры общего метода в этот класс. Но это решение не всегда является хорошим решением.

Ответ 3

Одна вещь, которая может помочь в некоторых случаях, когда вы хотите указать некоторые параметры типа, а другие должны быть определены, - это создать общий статический класс с параметрами, которые нужно указать, а затем внутри этого класса есть общий статический метод с параметры, которые вы хотите получить. Например, у меня есть метод, который, используя метод, который может быть преобразован в Action (T, U, V), вместе с T, будет генерировать Action (U, V), который вызовет этот делегат с изначально заданным T вместе с U и V. Метод будет вызываться как (синтаксис vb):

  NewAction = ActionOf(Of FooType, BarType).NewAction(AddressOf MyFunctionOfBozFooBar, someBoz)

Компилятор может определить один из параметров типового типа, используя тип someBoz, хотя он должен явно указывать параметры FooType и BarType.