C# lambda 表达式可以返回 void 吗?

发布于 2024-08-17 18:13:22 字数 1315 浏览 8 评论 0原文

我有以下方法,我想知道是否有任何东西可以放在下面的 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 的 lambda。似乎是说无法推断类型,但是当然,如​​果我这样指定类型,或者:

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

从语言设计的角度来看,我很好奇,为什么我们没有可以返回 void 或变量数据类型的表达式那是无效的。

I have the following method, and I want to know if there is anything that can go in place default(void) below because there is a compiler error that says that void is not valid here:

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

I realize that I can use the if-else statement instead of the ternary operator (or that I could call a dummy method to return void). Also, the Select extension method does not like lambdas that return void. It seems to say that the type cannot be inferred, but of course if I specify the type like this, either:

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

I was curious from a language design standpoint, why we don't have expressions that can return void or the data type for variables that is void.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

穿越时光隧道 2024-08-24 18:13:22

我建议您参阅规范的第 7.1 节,其中规定:

[表达式可以分类为]
“没有什么”。当
表达式是一个调用
返回类型为 void 的方法。
被分类为无的表达式仅在 a 的上下文中有效
语句表达。

[强调]。

也就是说,唯一可以使用返回 void 的方法调用的表达式是当该表达式构成整个语句时。像这样:

M();

I refer you to section 7.1 of the specification, which states:

[An expression may be classified as]
"nothing". This occurs when the
expression is an invocation of a
method with a return type of void.
An expression classified as nothing is only valid in the context of a
statement expression.

[Emphasis added].

That is to say that the only time you may use an expression which is a void-returning method invocation is when the expression makes up an entire statement. Like this:

M();
早茶月光 2024-08-24 18:13:22

实际上,这违反了函数式编程规则。这与 Eric Lippert 描述的 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);

This is, in effect, a violation of functional programming rules. This has the same flaw Eric Lippert described about List.ForEach: You're philosphically trying to cause side effects on your collection.

Enumerable.Select is intended to return a new collection - filtering the input. It is not intended to execute code.

That being said, you can work around this by doing:

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

It's just not as clear as doing:

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

但我真的不建议这样做。

Firstly, you should really avoid putting side-effects in standard linq query operators, and secondly this won't actually work since you aren't enumerating the Select query anywhere. If you want to use linq you could do this:

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

Regarding your question, I'm pretty sure there are no possible values of void and you can't return it explicity. In functional languages such as F#, void is replaced with 'unit' i.e. a type with only one possible value - if you wanted you could create your own unit type and return that. In this case you could do something like this:

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

But I really can't recommend doing this.

绝不放开 2024-08-24 18:13:22

从语言的角度来看,void 意味着“不存在”,这就引出了一个问题:声明一个不存在的变量会有什么价值?

这里的问题不是语言限制,而是您使用的构造(三元运算符)需要两个右值——而您只有一个右值。

如果我可以直言不讳的话,我会说你避免使用 if/else 来支持毫无意义的简洁。您想出的任何替换 default(void) 的技巧只会让其他开发人员感到困惑,或者在您不再为此类事情烦恼很久之后。

From a language standpoint, void means "does not exist", which begs the question: what value would there be in declaring a variable that does not exist?

The problem here is not a language limitation but the fact that you're using a construct (the ternary operator) that demands two rvalues -- where you only have one.

If I may be blunt, I'd say you're avoiding if/else in favor of pointless brevity. Any tricks you come up with to replace default(void) will only serve to confuse other developers, or you, in the future, long after you've stopped bothering with this sort of thing.

仅此而已 2024-08-24 18:13:22

默认值不适用于 void;但它适用于一种类型。 Action 类不产生结果,但 Func<> 产生结果。对象总是必须返回结果。无论 item.Value.Invoke() 返回什么,都只返回默认值,如:

default(object)

或如果它是特定类型:

default(SomeType)

类似。

default doesn't work with void; but it works with a type. The Action class produces no result, but the Func<> object always has to return a result. Whatever item.Value.Invoke() returns just return the default of that, as in:

default(object)

or if it's a specific type:

default(SomeType)

Like that.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文