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

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

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

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

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

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

10
задан Raghu Dodda 3 May 2018 в 23:31
поделиться

5 ответов

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

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

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

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

M();
10
ответ дан 3 December 2019 в 15:22
поделиться

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

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

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

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

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

4
ответ дан 3 December 2019 в 15:22
поделиться

Это, по сути, является нарушением функциональных правил программирования. Это имеет тот же самый недостаток , который Эрик Липперт описал в 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);
10
ответ дан 3 December 2019 в 15:22
поделиться

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

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

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

3
ответ дан 3 December 2019 в 15:22
поделиться

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

default(object)

или если это специфический тип:

default(SomeType)

Like that.

2
ответ дан 3 December 2019 в 15:22
поделиться
Другие вопросы по тегам:

Похожие вопросы: