lambda 表达式中的 C# 切换

Posted

技术标签:

【中文标题】lambda 表达式中的 C# 切换【英文标题】:C# switch in lambda expression 【发布时间】:2010-11-28 19:26:47 【问题描述】:

是否可以在 lambda 表达式中进行切换?如果不是,为什么? Resharper 将其显示为错误。

【问题讨论】:

它可以编译吗?我不会使用 Resharper 作为您对有效语法的最终决定。 当然不是,编译后的错误与 resharper 显示的输出相同:无法将带有语句体的 lambda 表达式转换为表达式树。 注:我不知道什么是语句体。 “我不知道语句体是什么”——这就是以 / 大括号开头的 lambda 之间的区别(它 always 编译为匿名方法),以及不支持的 lambda(可以编译为匿名方法表达式树) @Jeff - 当然,编译器也不是最终的决定 - 但它肯定比尝试检查规范更容易;-p 特别是表达式树翻译的细节是众所周知,在 C# 3.0 / C# 4.0 规范中缺失。 【参考方案1】:

你可以在语句块中使用 lambda:

Action<int> action = x =>

  switch(x)
  
    case 0: Console.WriteLine("0"); break;
    default: Console.WriteLine("Not 0"); break;
  
;

但你不能在“单一表达式 lambda”中做到这一点,所以这是无效的:

// This won't work
Expression<Func<int, int>> action = x =>
  switch(x)
  
    case 0: return 0;
    default: return x + 1;
  ;

这意味着您不能在表达式树中使用 switch(至少由 C# 编译器生成;我相信 .NET 4.0 至少在库中支持它)。

【讨论】:

是的,确实似乎问题是我想要一个表达式树。有没有比 if els if o a functino 更好的解决方法? 一个小修正(事后看来……后见之明有多大好处:-))。 C# 4.0 编译器仍然无法构建“复杂”Expressions(使用 ifwhile...),但您可以通过使用 Expression 类来构建它们。这两件事是解耦的。事后诸葛亮:OP 询问了表达式树。第二个 action 应该是 Expression&lt;Func&lt;int, int&gt;&gt; 以表明您确实需要表达式树而不是委托。 已经有一段时间了,C# 8 添加了 switch 表达式,允许您使用 Func&lt;int, int&gt; action = x =&gt; x switch 0 =&gt; 0, _ =&gt; x + 1 ; 但是,由于某种原因,它仍然不允许在表达式树中使用 Expression&lt;Func&lt;int, int&gt;&gt; action = x =&gt; x switch 0 =&gt; 0, _ =&gt; x + 1 ;【参考方案2】:

在纯 Expression(在 .NET 3.5 中)中,您可以获得的最接近的是复合条件:

    Expression<Func<int, string>> func = x =>
        x == 1 ? "abc" : (
        x == 2 ? "def" : (
        x == 3 ? "ghi" :
                 "jkl")); /// yes, this is ugly as sin...

不好玩,尤其是当它变得复杂时。如果您的意思是带有语句体的 lamda 表达式(仅用于 LINQ-to-Objects),那么大括号内的任何内容都是合法的:

    Func<int, string> func = x => 
        switch (x)
            case 1:  return "abc";
            case 2:  return "def";
            case 3:  return "ghi";
            default: return "jkl";
        
    ;

当然,您可以将工作外包;例如,LINQ-to-SQL 允许您将标量 UDF(在数据库中)映射到数据上下文(实际未使用)上的方法 - 例如:

var qry = from cust in ctx.Customers
          select new cust.Name, CustomerType = ctx.MapType(cust.TypeFlag) ;

其中MapType 是在数据库服务器上执行工作的 UDF。

【讨论】:

你可以在你的第一个代码中省略括号 - 结果是 much 不那么难看,我有时会使用它。诚然,这需要一点时间来适应,但它的可读性并不比同等的 switch 块低(我认为不那么好)。 没有括号我有时会迷路? / : 等等...但是是的,没有它们它也可以工作;-p @MarcGravell 对于.Net 4.0 中的纯Expression 来说还有什么更好的吗? @ken2k 是和否; Expression API 包括完整的块支持,包括 Expression.Switch - 但是,这里的问题是关于 lambdas; C# lambda-to-Expression 编译器没有改变,现在支持更广泛的Expression 语法。 @MarcGravell 感谢您提供信息。我将使用您在第一个代码中提供的语法,这是我发现的最易读的缩进:) 让我们希望 C# 语言/编译器的下一个版本能够提供更好的东西。【参考方案3】:

是的,它可以工作,但是您必须将代码放在一个块中。示例:

private bool DoSomething(Func<string, bool> callback)

    return callback("FOO");

然后,调用它:

DoSomething(val =>
                
                    switch (val)
                    
                        case "Foo":
                            return true;

                        default:
                            return false;
                    
                );

【讨论】:

【参考方案4】:

嗯,我看不出这不应该起作用的原因。请注意您使用的语法

param => 
    // Nearly any code!


delegate (param) 
    // Nearly any code!


param => JustASingleExpression (No switches)

【讨论】:

【参考方案5】:

我也查过:-)

[Test]
public void SwitchInLambda()

    TakeALambda(i => 
        switch (i)
        
            case 2:
                return "Smurf";
            default:
                return "Gnurf";
        
    );


public void TakeALambda(Func<int, string> func)

    System.Diagnostics.Debug.WriteLine(func(2));

工作得很好(输出“Smurf”)!

【讨论】:

因为那不是 Lambda 表达式。 Lambda 表达式 的类型为Expression&lt;Func&lt;int, string&gt;&gt;。那是一个 Lambda 函数。尝试更改 TakeALambda 的输入参数,看看有什么不同。【参考方案6】:

我刚学这个:

                      (model) =>
                                
                                    switch(model.SentInvoiceTypeId)
                                    
                                        case 1:
                                            return "1 asdf";
                                        case 2:
                                            return "2 asdf";
                                        case 3:
                                            return "3 asdf ";
                                        case 4:
                                            return "4 asdf ";
                                        default:
                                            return "asdf";
                                    
                                

只需放在“模型”() 之间并在 中添加您的代码,记得有一个返回。 我不确定在哪个版本的 C# 中可以工作,在这个例子中是 C# 7.0

我希望这个答案可以帮助别人。

【讨论】:

以上是关于lambda 表达式中的 C# 切换的主要内容,如果未能解决你的问题,请参考以下文章

C# 闭包中的 Lambda 表达式是啥?

C# 闭包中的 Lambda 表达式是啥?

C#中的Lambda表达式[重复]

c# 中的 Lambda 表达式

C#中的委托,匿名方法和Lambda表达式

如何解决 C# 中 linq 的 lambda 表达式中的对象引用错误?