Может ли C # лямбда-выражение когда-либо возвращать void? - PullRequest
9 голосов
/ 08 января 2010

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

private void applyDefaultsIfNecessary(ApplicationConfiguration configuration)
{
    var defaults = new Dictionary<Predicate<ApplicationConfiguration>, Action<ApplicationConfiguration>>()
    {
       // { rule, action } - if rule is true, execute action 
       { (c) => c.ConnectionString == null , (c) => c.ConnectionString = "foo" },
       { (c) => c.OutputExcelFilePath == null, (c) => c.ConnectionString = "bar" },
       { (c) => c.OutputDirectory == null, (c) => c.OutputDirectory = "baz" }

    };

    //Nothing to select, but we want to loop throough the dict and invoke action, if rule is true.
    //It is a pity there is no extension method called DoForEach on collections.
    defaults.Select((item) => item.Key.Invoke(configuration) ? item.Value.Invoke(configuration) : default(void)  );
}

Я понимаю, что могу использовать оператор if-else вместо тернарного оператора (или что я мог бы вызвать фиктивный метод для возврата void). Кроме того, метод расширения Select не любит лямбда-выражения, которые возвращают void. Кажется, говорят, что тип не может быть выведен, но, конечно, если я укажу тип как этот, либо:

defaults.Select<ApplicationConfiguration, void>((item) => { if (item.Key.Invoke(configuration)) item.Value.Invoke(configuration); } );

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

Ответы [ 5 ]

10 голосов
/ 08 января 2010

Я отсылаю вас к разделу 7.1 спецификации, в котором говорится:

[Выражение может быть классифицировано как] "ничего такого". Это происходит, когда Выражение является вызовом метод с возвращаемым типом void. Выражение, классифицированное как ничто, является действительным только в контексте Выражение оператора.

[Акцент добавлен].

То есть единственный раз, когда вы можете использовать выражение, которое является вызовом метода, возвращающего пустоту, - это когда выражение составляет весь оператор . Как это:

M();
9 голосов
/ 08 января 2010

Это, по сути, нарушение правил функционального программирования. В этом есть тот же недостаток Эрик Липперт, описанный в отношении List.ForEach : Вы философски пытаетесь вызвать побочные эффекты в своей коллекции.

Enumerable.Select предназначен для возврата новой коллекции - фильтрации входных данных. Он не предназначен для выполнения кода.

Как говорится, вы можете обойти это, выполнив:

defaults.Where(item => item.Key.Invoke(configuration)).ToList().ForEach( item => item.Value.Invoke(configuration));

Это не так ясно, как делать:

var matches = defaults.Where(item => item.Key.Invoke(configuration));
foreach(var match in matches)
    match.Value.Invoke(configuration);
3 голосов
/ 08 января 2010

Во-первых, вам действительно следует избегать добавления побочных эффектов в стандартные операторы запросов linq, а во-вторых, это не сработает, поскольку вы нигде не перечисляете запрос Select. Если вы хотите использовать linq, вы можете сделать это:

foreach(var item in defaults.Where(i => i.Key.Invoke(configuration)))
{
   item.Value.Invoke(configuration);   
}

Относительно вашего вопроса, я почти уверен, что нет возможных значений void, и вы не можете вернуть его подробно. В функциональных языках, таких как F #, void заменяется на 'unit', то есть тип только с одним возможным значением - если вы хотите, вы можете создать свой собственный тип модуля и вернуть его. В этом случае вы можете сделать что-то вроде этого:

defaults.Select(item => {
    if(item.Key.Invoke(configuration))
    {
        item.Value.Invoke(configuration);
    }
    return Unit.Value;
}).ToList();

Но я действительно не могу рекомендовать это.

3 голосов
/ 08 января 2010

С языковой точки зрения void означает «не существует», что вызывает вопрос: какое значение будет иметь объявление переменной, которая не существует?

Проблема здесь не в языковом ограничении, а в том, что вы используете конструкцию (троичный оператор), которая требует двух значений - где у вас есть только одно.

Если бы я был тупым, я бы сказал, что вы избегаете, если / в пользу бессмысленной краткости. Любые уловки, которые вы придумаете, чтобы заменить default(void), будут только вводить в заблуждение других разработчиков или вас, в будущем, после того, как вы перестанете беспокоиться о подобных вещах.

2 голосов
/ 08 января 2010

по умолчанию не работает с void; но это работает с типом. Класс Action не дает результата, но объект Func <> всегда должен возвращать результат. Что бы не возвращал item.Value.Invoke (), просто верните значение по умолчанию, как в:

по умолчанию (объект)

или, если это определенный тип:

по умолчанию (SomeType)

Вот так.

...