Почему анонимные делегаты/лямбды не выводят типы на/касательно параметрах?

Несколько вопросов C# на StackOverflow спрашивают, как сделать анонимных делегатов/лямбды с out или ref параметры. Посмотрите, например:

Для этого просто необходимо указать тип параметра, как в:

public void delegate D(out T p);
// ...
D a = (out T t) => { ... };      // Lambda syntax.
D b = delegate(out T t) { ... }; // Anonymous delegate syntax.

То, на предмет чего мне любопытно, - то, почему тип явно требуется. Существует ли конкретная причина это дело обстоит так? Таким образом, с точки зрения компилятора/языка, почему следующее не позволяется?

D a = (out t) => { ... };      // Lambda syntax -- implicit typing.
D b = delegate(out t) { ... }; // Anonymous delegate syntax -- implicit typing.

или еще лучше, просто:

D a = (t) => { ... };      // Lambda syntax -- implicit typing and ref|out-ness.
D b = delegate(t) { ... }; // Anonymous delegate syntax -- implicit typing and ref|out-ness.

16
задан Community 23 May 2017 в 10:29
поделиться

2 ответа

Интересный вопрос.

Во-первых, рассмотрим разницу между анонимными методами и лямбдами. С точки зрения автора компилятора, главное отличие состоит в том, что lambdas может потребовать от компилятора сделать вывод о типе параметров, с которых присваивается лямбда; в C# 2 анонимные методы такой возможности не имеют. Эта особенность кажется небольшой разницей, но на самом деле она имеет серьезные последствия для реализации компилятора. Смотрите серию моих блогов на эту тему для размышлений о том, почему это:

http://blogs.msdn.com/ericlippert/archive/2007/01/10/lambda-expressions-vs-anonymous-methods-part-one.aspx

Итак, теперь давайте перейдем к вашему фактическому вопросу: почему мы не можем сделать вывод об outness/refness из типа-мишени к параметрам лямбды. То есть, если мы делегировали void D(out int x), то наверняка D d = x=> { x = 10; } может сделать вывод, что x - это "out int".

Нет никаких технических причин, по которым я знаю, почему мы не могли этого сделать. Внутри компилятора типы out/ref представлены как типы, как и любые другие.

Однако, возможности делаются не только потому, что их можно сделать; они делаются потому, что для этого есть веская причина. Для lambdas убедительной причиной для вывода типа в первую очередь является LINQ; мы хотим иметь возможность сделать простое синтаксическое преобразование при понимании запроса в вызов метода с помощью lambdas, и пусть механизм вывода типа метода отработает типы всех лямбда-параметров. Ни один из сгенерированных LINQ-методов не имеет делегатов с параметрами out или ref.

Таким образом, у нас нет веских причин делать эту функцию. Делегаты, которые имеют out/ ref параметры, встречаются относительно редко. И назначение лямбда этим делегатам встречается все реже. Так что это функция, которая нам не нужна, и которая почти никому не нужна.

C# 3 был "длинным полюсом" в расписании Visual Studio; у нас было наибольшее количество дней работы из любой команды, которая отгружает компонент в VS. Это означало, что каждый день мы проскальзывали по расписанию, весь отдел проскальзывал. Это было мощным сдерживающим фактором для того, чтобы тратить время на ненужные функции, которые никому не приносили пользы. Так что работа никогда не была выполнена.

Я согласен, что было бы неплохо быть более последовательным здесь, но это вряд ли произойдет. У нас есть много более высоких приоритетов.

17
ответ дан 30 November 2019 в 22:24
поделиться

Из комментария Эрика Липперта о том, почему объявление и присвоение переменной var нельзя разделить:

Я согласен, что в принципе это можно было бы сделать, но на практике это гораздо сложнее, чем может показаться на примере вашего быстрого наброска. var не только требует наличия инициализатора, он также требует, чтобы инициализатор не ссылался на переменную. Если у вас есть int M(out int), то вы можете сказать "int x = M(out x);", но вы не можете сказать "var x = M(out x);", потому что для того, чтобы сделать разрешение перегрузки на M, нам нужно знать тип x, который мы пытаемся выяснить. Будет ли законным сказать "var s; if (b) M(out s); else s = 0;" ?

Полагаю, ответ на ваш вопрос похож, учитывая, например,

D a = (out var x) => x = M(out x);
2
ответ дан 30 November 2019 в 22:24
поделиться
Другие вопросы по тегам:

Похожие вопросы: