Вопрос

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

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;
};

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

Это было полезно?

Решение

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

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

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

Там нет никакого Func<T> тип, который принимает ссылку на что угодно.

var x2 = y=>123;

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

var x3 = (int y)=>null;

Мы не знаем возвращаемый тип, но он не может быть void.Возвращаемый тип может быть любым ссылочным типом или любым типом значения с нулевым значением.

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

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

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

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

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

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

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

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

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

и теперь вывод типа метода определяет, что такое тип функции.

Другие советы

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

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

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

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

Eric Lippert имеет старый пост об этом, где он говорит

и на самом деле спецификация C # 2.0 называет это.Группа методов Выражения и анонимный метод Выражения - это опасные выражения в C # 2.0 и лямбда выражения присоединяются их в C # 3.0.Поэтому это незаконно для них появляться «голые» на правая сторона неявного Декларация.

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

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

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

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

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

    Ссылка MSDN: неявно набранные локальные переменные

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

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

      MSDN Ссылка: анонимные методы

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

Мой пост не отвечает на сам вопрос, но он отвечает на основной вопрос о :

"Как мне избежать необходимости набирать какой-нибудь беглый текст, например 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 в качестве параметра, в этом случае, вам нужно будет ввести эту беглую строку :/

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

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);
.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top