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

StackOverflow https://stackoverflow.com/questions/2029433

Вопрос

У меня есть следующий метод, и Я хочу знать, есть ли что-нибудь, что может заменить default (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 .

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

Решение

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

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

[Курсив добавлен].

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

M();

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

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

Перечислимый.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);

Во-первых, вам действительно следует избегать использования побочных эффектов в стандартных операторах запросов 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();

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

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

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

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

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

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

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

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

Вот так.

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