Почему анонимный метод не может быть назначен на var? - PullRequest
131 голосов
/ 11 февраля 2011

У меня есть следующий код:

Func<string, bool> comparer = delegate(string value) {
    return value != "0";
};

Однако следующее не компилируется:

var comparer = delegate(string value) {
    return value != "0";
};

Почему компилятор не может понять, что это Func<string, bool>? Он принимает один строковый параметр и возвращает логическое значение. Вместо этого он дает мне ошибку:

Невозможно назначить анонимный метод неявно типизированная локальная переменная.

У меня есть одно предположение, и это , если версия var скомпилирована , будет непоследовательностью, если у меня будет следующее:

var comparer = delegate(string arg1, string arg2, string arg3, string arg4, string arg5) {
    return false;
};

Вышеприведенное не имеет смысла, поскольку Func <> допускает только до 4 аргументов (в .NET 3.5, который я использую). Возможно, кто-то мог бы прояснить проблему. Спасибо.

Ответы [ 7 ]

148 голосов
/ 11 февраля 2011

Другие уже указывали, что существует бесконечно много возможных типов делегатов, которые вы могли бы иметь в виду;Что такого особенного в Func, что оно заслуживает того, чтобы быть значением по умолчанию вместо Predicate или Action или любой другой возможности?И, что касается лямбд, почему очевидно, что цель состоит в том, чтобы выбрать форму делегата, а не форму дерева выражений?

Но мы могли бы сказать, что Func является особенным, и что предполагаемый типЛямбда или анонимный метод - это функция чего-то.У нас все еще были бы все виды проблем.Какие типы вы хотели бы получить в следующих случаях?

var x1 = (ref int y)=>123;

Не существует типа Func<T>, который принимает ref что-либо.

var x2 = y=>123;

Мы не знаемтип формального параметра, хотя мы знаем возврат.(Или мы? Является ли возвращаемое int длинным коротким байтом?)

var x3 = (int y)=>null;

Мы не знаем тип возвращаемого значения, но оно не может быть недействительным.Тип возвращаемого значения может быть любым ссылочным типом или любым типом значения, допускающим значение NULL.

var x4 = (int y)=>{ throw new Exception(); }

Опять же, мы не знаем тип возвращаемого значения, и на этот раз может быть недействительным.

var x5 = (int y)=> q += y;

Является ли это лямбда-оператором, возвращающим пустоту, или чем-то, что возвращает значение, присвоенное q?Оба законны;какой из них мы должны выбрать?

Теперь, вы можете сказать, ну, просто не поддерживаете ни одну из этих функций.Просто поддерживайте «нормальные» случаи, когда типы могут быть проработаны.Это не помогаетКак это облегчает мою жизнь?Если функция иногда срабатывает и иногда дает сбой, тогда мне все равно нужно написать код для обнаружения всех этих ситуаций отказа и выдать значимое сообщение об ошибке для каждой из них.Нам все еще нужно указать все это поведение, задокументировать его, написать тесты для него и так далее.Это очень дорогая функция , которая экономит пользователю, возможно, полдюжины нажатий клавиш.У нас есть лучшие способы повысить ценность языка, чем тратить много времени на написание контрольных примеров для функции, которая не работает в половине случаев и практически не дает никаких преимуществ в тех случаях, когда она работает.

Ситуация, когда это действительно полезно:

var xAnon = (int y)=>new { Y = y };

, потому что для этой вещи нет «говорящего» типа.Но у нас есть эта проблема все время, и мы просто используем вывод типа метода, чтобы вывести тип:

Func<A, R> WorkItOut<A, R>(Func<A, R> f) { return f; }
...
var xAnon = WorkItOut((int y)=>new { Y = y });

, и теперь вывод типа метода определяет, что такое тип func.

29 голосов
/ 11 февраля 2011

Только Эрик Липперт знает наверняка, но я думаю, что это потому, что сигнатура типа делегата не определяет тип однозначно.

Рассмотрим ваш пример:

var comparer = delegate(string value) { return value != "0"; };

Вотдва возможных вывода того, каким должен быть var:

Predicate<string> comparer  = delegate(string value) { return value != "0"; };  // okay
Func<string, bool> comparer = delegate(string value) { return value != "0"; };  // also okay

Какой вывод должен сделать компилятор?Там нет веских причин, чтобы выбрать один или другой.И хотя Predicate<T> функционально эквивалентен Func<T, bool>, они по-прежнему являются разными типами на уровне системы типов .NET.Следовательно, компилятор не может однозначно разрешить тип делегата и должен завершить вывод типа.

6 голосов
/ 11 февраля 2011

Эрик Липперт имеет старый пост о нем, где он говорит

А на самом деле спецификация C # 2.0 вызывает это. Метод группы выражения и анонимный метод выражения являются типичными выражениями в C # 2.0 и лямбда-выражения присоединяются их в C # 3.0. Поэтому это незаконно для них появляться "голым" на правая часть неявного декларация.

5 голосов
/ 11 февраля 2011

Разные делегаты считаются разными типами.например, Action отличается от MethodInvoker, и экземпляр Action не может быть назначен переменной типа MethodInvoker.

Так, учитывая анонимный делегат (или лямбду), как() => {}, это Action или MethodInvoker?Компилятор не может сказать.

Аналогичным образом, если я объявлю тип делегата, принимающий аргумент string и возвращающий bool, как компилятор узнает, что вы действительно хотите Func<string, bool> вместо моего делегататип?Он не может определить тип делегата.

2 голосов
/ 11 февраля 2011

Следующие пункты взяты из MSDN относительно неявно типизированных локальных переменных:

  1. var можно использовать только тогда, когда локальная переменная объявлена ​​и инициализирована в том же операторе; переменная не может быть инициализирована нулем, или группой методов, или анонимной функцией.
  2. Ключевое слово var указывает компилятору выводить тип переменной из выражения в правой части оператора инициализации.
  3. Важно понимать, что ключевое слово var не означает «вариант» и не указывает на то, что переменная слабо типизирована или имеет позднюю привязку. Это просто означает, что компилятор определяет и назначает наиболее подходящий тип.

Справочник MSDN: неявно типизированные локальные переменные

Учитывая следующее относительно анонимных методов:

  1. Анонимные методы позволяют вам опустить список параметров.

Справочник по MSDN: анонимные методы

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

1 голос
/ 08 ноября 2018

Мой пост не отвечает на реальный вопрос, но он отвечает на основной вопрос:

"Как мне избежать необходимости печатать какой-то неаккуратный тип, такой как Func<string, string, int, CustomInputType, bool, ReturnType>?" [1]

Будучи ленивым / хакерским программистом, я экспериментировал с использованием Func<dynamic, object> - который принимает один входной параметр и возвращает объект.

Для нескольких аргументов вы можете использовать его следующим образом:

dynamic myParams = new ExpandoObject();
myParams.arg0 = "whatever";
myParams.arg1 = 3;
Func<dynamic, object> y = (dynObj) =>
{
    return dynObj.arg0.ToUpper() + (dynObj.arg1 * 45); //screw type casting, amirite?
};
Console.WriteLine(y(myParams));

Совет: Вы можете использовать Action<dynamic>, если вам не нужно возвращать объект.

Да, я знаю, что это возможноидет вразрез с вашими принципами программирования, но это имеет смысл для меня и, возможно, для некоторых программистов Python.

Я довольно новичок в делегатах ... просто хотел поделиться тем, что я узнал.* [1] Предполагается, что вы не вызываете метод, для которого в качестве параметра требуется предопределенный Func, и в этом случае вам придется набрать эту беспорядочную строку: /

0 голосов
/ 19 декабря 2013

Как насчет этого?

var item = new
    {
        toolisn = 100,
        LangId = "ENG",
        toolPath = (Func<int, string, string>) delegate(int toolisn, string LangId)
        {
              var path = "/Content/Tool_" + toolisn + "_" + LangId + "/story.html";
              return File.Exists(Server.MapPath(path)) ? "<a style=\"vertical-align:super\" href=\"" + path + "\" target=\"_blank\">execute example</a> " : "";
        }
};

string result = item.toolPath(item.toolisn, item.LangId);
...