Почему анонимные делегаты / лямбда-выражения не выводят типы по параметрам out / ref?

В нескольких вопросах 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.

person John Feminella    schedule 02.01.2010    source источник
comment
Мне самому это интересно. Будем надеяться, что Эрик Липперт заметит этот пост ... он, скорее всего, сможет дать содержательный ответ на этот вопрос.   -  person LBushkin    schedule 02.01.2010
comment
Л.Бушкин, если вы хотите что-то довести до моего сведения, вы всегда можете прислать это мне по контактной ссылке в моем блоге.   -  person Eric Lippert    schedule 02.01.2010
comment
Для анонимных методов с ключевым словом delegate правило состоит в том, что вы либо ничего не указываете (оставляете даже круглые скобки ()), либо указываете полную сигнатуру, включая типы параметров и модификаторы, такие как _3 _ / _ 4_. Так что _5 _ / _ 6_ не исключение. Что касается лямбда-выражений, ваша идея имеет смысл, но я не уверен, что считаю ее хорошей. Синтаксис (out t) => { ... } делает вид, что это тип параметра. Тогда t => { ... } лучше, но это может быть опасно, если кто-то забудет значение D и отредактирует тело { ... } лямбды, не осознавая out природу t.   -  person Jeppe Stig Nielsen    schedule 22.06.2013


Ответы (2)


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

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

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

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

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

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

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

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

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

person Eric Lippert    schedule 02.01.2010
comment
Я думаю, что все прекрасно знают, насколько вы заняты, ребята, но определенно приятно знать, что дверь не закрыта по техническим причинам, и что теоретически это возможно когда-нибудь. Спасибо за вклад, Эрик! - person John Feminella; 03.01.2010

Из комментария Эрика Липперта о том, почему объявление и присвоение переменной 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);
person dtb    schedule 02.01.2010
comment
Я не уверен. Как вы объясните тот факт, что если вы удалите ключевое слово out, компилятор сможет сделать вывод? Тип делегата D должен полностью определять тип x. - person LBushkin; 02.01.2010
comment
Я не уверен, что это так. Здесь мы знаем, что пытаемся закончить с чем-то похожим на D. Если результат справа не совпадает, разве это не очевидно? В примере var это может быть что угодно, поэтому компилятор не имеет никаких подсказок о том, как должен выглядеть правильный ответ. [Упс! Л.Бушкин меня опередил!] - person John Feminella; 02.01.2010