有没有办法声明一个 C# lambda 并立即调用它?

Glo*_*del 29 c# lambda

可以声明一个 lambda 函数并立即调用它:

Func<int, int> lambda = (input) => { return 1; };
int output = lambda(0);
Run Code Online (Sandbox Code Playgroud)

我想知道是否可以在一行中这样做,例如

int output = (input) => { return 1; }(0);
Run Code Online (Sandbox Code Playgroud)

这给出了编译器错误“预期的方法名称”。投射到Func<int, int>也不起作用:

int output = (Func<int, int>)((input) => { return 1; })(0);
Run Code Online (Sandbox Code Playgroud)

给出了相同的错误,并且由于下面提到的原因,我想避免必须明确指定输入参数类型(第一个int)。


您可能想知道为什么我要这样做,而不是直接嵌入代码,例如int output = 1;. 原因如下:我用 生成了一个 SOAP 网络服务的引用svcutil,因为嵌套元素生成了非常长的类名,我想避免输入。所以代替

var o = await client.GetOrderAsync(request);
return new Order {
    OrderDate = o.OrderDate,
    ...
    Shipments = o.Shipment_Order == null ? new Shipment[0]
        o.Shipment_Order.Select(sh => new Shipment {
            ShipmentID = sh.ShipmentID,
            ...
            Address = CreateAddress(sh.ReceiverAddress_Shipment);
        }).ToArray()
};
Run Code Online (Sandbox Code Playgroud)

和一个单独的CreateAddress(GetOrderResultOrderShipment_OrderShipmentShipment_Address address)方法(实名更长,我对表格的控制非常有限),我想写

var o = await client.GetOrderAsync(request);
return new Order {
    OrderDate = o.OrderDate,
    ...
    Shipments = o.Shipment_Order == null ? new Shipment[0]
        o.Shipment_Order.Select(sh => new Shipment {
            ShipmentID = sh.ShipmentID,
            ...
            Address = sh.ReceiverAddress_Shipment == null ? null : () => {
                var a = sh.ReceiverAddress_Shipment.Address;
                return new Address {
                    Street = a.Street
                    ...
                };
            }()
        }).ToArray()
};
Run Code Online (Sandbox Code Playgroud)

我知道我可以写

Address = sh.ReceiverAddress_Shipment == null ? null : new Address {
    Street = sh.ReceiverAddress_Shipment.Address.Street,
    ...
}
Run Code Online (Sandbox Code Playgroud)

sh.ReceiverAddress_Shipment.Address如果有很多字段,即使是(部分)也会变得非常重复。声明一个 lambda 并立即调用它会更优雅地编写更少的字符。

ger*_*rmi 30

我建议您使用一个小的辅助函数,而不是尝试转换 lambda:

public static TOut Exec<TIn, TOut>(Func<TIn, TOut> func, TIn input) => func(input);
Run Code Online (Sandbox Code Playgroud)

然后你可以使用这样的:int x = Exec(myVar => myVar + 2, 0);。这对我来说比这里建议的替代方案要好得多。


Joh*_*lay 26

这很丑陋,但有可能:

int output = ((Func<int, int>)(input => { return 1; }))(0);
Run Code Online (Sandbox Code Playgroud)

匿名函数(包括 lambda 表达式)可隐式转换为与其签名匹配的委托,但此语法要求将 lambda 括在括号中。

上面的也可以简化为:

int output = ((Func<int, int>)(input => 1))(0);
Run Code Online (Sandbox Code Playgroud)

  • 啊,当然。我只尝试了 `int output = (Func&lt;int&gt;)(() =&gt; { return 1; })();` 但强制转换的优先级低于 lambda 执行。 (2认同)

Jör*_*tag 5

C# 中的 Lambda 文字有一个奇怪的区别,因为它们的含义取决于它们的类型。它们的返回类型本质上是重载的,这是 C# 中其他任何地方都不存在的。(数字文字有些相似。)

完全相同lambda 文字可以计算为您可以执行的匿名函数(即 / ),可以计算为 Body 内部操作的抽象表示,有点像抽象语法树(即 LINQ 表达式树)。FuncAction

例如,后者是 LINQ to SQL、LINQ to XML 等的工作方式:lambda计算为可执行代码,它们计算为 LINQ 表达式树,然后 LINQ 提供程序可以使用这些表达式树来了解lambda 的主体正在执行并从中生成例如 SQL 查询。

在您的情况下,编译器无法知道 lambda 文字是否应该计算为FuncLINQ 表达式或 LINQ 表达式。这就是Johnathan Barclay 的答案有效的原因:它为 lambda 表达式提供了一个类型,因此编译器知道您需要一个执行Funclambda 主体的已编译代码,而不是表示内部代码的未评估的 LINQ 表达式树lambda 的主体。