Самоназывающие анонимные функции - программирование
Подтвердить что ты не робот

Самоназывающие анонимные функции

В JavaScript нередко можно увидеть самозапускаемые функции:

var i = (function(x) {
    return x;
})(42);

// i == 42

В то время как я, конечно, не сравниваю языки, я решил, что такая конструкция будет переводимой на С#, при условии поддержки языковой версии:

var i = (delegate(int x) {
    return x;
})(42);

Или:

var i = ((x) => {
    return x;
})(42);

Или даже:

var i = (x => x)(42);

Однако каждая версия имеет ошибку:

Ожидаемое имя метода

Являются ли самозаверяющие анонимные методы неподдерживаемыми (из-за явного запрета или невозможности ввода типа), или есть ли ошибка в моих попытках?

Я осмеливаюсь предположить, что из-за отсутствия объявления метода (Func<T,T>), против которого можно было бы вывести типы, он не может сортировать подразумеваемые типы, цифры, которые я хотел назвать объявленным методом по имени, и действительно разобрался в синтаксисе.


Опечатки

Прежде чем этот вопрос залит:

var i = new Func<int, int>(x => x)(42);

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

Итак, немного выяснив вопрос; мы знаем, что мы можем var i = new Func<int, int>(x => x)(42);, но без создания экземпляра или литья на Func, возможно ли это?

Use-Case

Для любопытных (или заинтересованных) вариант использования был примерно таким:

var o = new {
    PropertyA = () => {
        // value computation
    }(),
    PropertyB = () => {
        // value computation
    }(),
};
4b9b3361

Ответ 1

Я полагаю, что кто-то из команды С# может дать гораздо лучший ответ, но я пробую свой выстрел. Вопрос здесь не в том, что это лучшая практика или нет, но если это возможно, а если нет, то почему.

Начнем с того, что вы напишете:

var i = (x => x)(42);

Довольно просто, вы передаете целое число (42) в лямбда, оно играет с ним, и оно возвращает то же значение. Его параметр является целым числом (выведенным из его использования), он возвращает целое число (выведенное из выражения), а i - другое целое число (выведенное из возвращаемого значения).

К сожалению, это нарушается на первом этапе. Позвольте мне привести большой Джон:

Компилятор пытается выработать типы параметров для лямбда-выражений на основе контекста, в котором он использовался.

Что это значит? На самом деле компилятор нуждается в некоторой информации, но он может использовать только:

  • Явное выражение, конструктор или объявление, например, в (Func<int, int>)(x => x)) или new Func<int, int>(x => x). Необходимые типы здесь даются или выводятся с использованием требуемого конечного типа.
  • Назначение (опять же потому, что конечный тип может быть выведен), как в: Func<int, int> f = (x => x);

В вашем случае компилятор должен вывести тип функции из своих параметров, и это невозможно, потому что параметры проверяются против выражения, а не наоборот.

Почему это невозможно в С#? Поскольку (x => x) является просто делегатом, то он может быть любым делегатом, который принимает целочисленный параметр (если мы предположим, что компилятор может вывести lhs из rhs и проверить rhs согласно lhs) и возвращает другое целое число. Фактически преобразование между делегатами не разрешено в С#, поэтому выражение недействительно (даже если этот специальный делегат не будет назначен какой-либо переменной, и он не будет использоваться в будущем). Это может быть Func<int, int> или Expression<Func<int, int>> или любой другой делегат (для параметра bool он может быть даже bee Predicate<bool>).

Конечно, это решение для дизайна С#, то же выражение - например, - в VB.NET совершенно верно:

 Dim i = (Function(x) x)(42)

Разный язык, разные правила для подчинения, цель С# - избежать такой двусмысленности.

Ответ 2

var x = ((Func<int, int>)(y => y * 2))(10);

Проблема заключается в том, что, когда компилятор видит y => y * 2, он по умолчанию классифицирует его как Expression вместо Func, если нет какой-либо контекстной информации, чтобы сообщить ему, что это должно быть Func. Отбрасывая его на Func, мы даем ему контекст, в котором он нуждается.