我最近在 C# 中看到以下编译器消息:
CS9236 编译需要至少绑定 lambda 表达式 100 次。请考虑使用显式参数类型声明 lambda 表达式,或者如果包含的方法调用是泛型的,请考虑使用显式类型参数。
不幸的是,帮助链接(目前)已损坏。您能举例解释一下吗?为什么用显式类型来扩大表达式是个好主意?
中的一个例子:
using System.Linq;
class Container
{
public IEnumerable<Container> Items;
public int Value;
}
class Program
{
static void Main()
{
var list = new List<Container>();
_ = list.Sum(
a => a.Items.Sum(
b => b.Items.Sum(
c => c.Value)));
}
}
18
最佳答案
1
首先,据我所知,这些消息仅出现在 Visual Studio 中 – 可能有办法dotnet build
报告它们,但我还没有找到。
该示例可以稍微简化,仅使用两个 lambda 表达式:
#pragma warning disable 649
class Container
{
public IEnumerable<Container> Items;
public int Value;
}
class Program
{
static void Main()
{
var list = new List<Container>();
_ = list.Sum(
a => a.Items.Sum(
b => b.Value));
}
}
这仅会产生一条消息,但更容易理解。
该消息与代码是否有效无关 – 它实际上只是说“嘿,这给编译器带来了很多工作 – 你可以让它的工作更轻松。” 在某些情况下,这反过来可以减少编译时间。
发生这种情况的原因在于 C# 的两个最复杂的方面(从规范角度来看,我怀疑从编译器实现角度来看):
- 泛型类型推断
- 重载决策
具体来说,虽然 C# 中的大多数表达式自然具有类型,但对于 lambda 表达式,编译器必须有效地查看它是否可以将给定的 lambda 表达式转换为候选参数类型,以检查每个重载的适用性。当存在嵌套的lambda 时,这意味着整个过程最终会呈指数级增长。
Sum
导致此问题的原因Select
是(比如说)没有– 即使您使用多个参数切断过载。
对于此示例,该消息有点令人困惑,因为指定 lambda 表达式参数类型和类型参数没有帮助。这仍然给出相同的消息,例如:
_ = list.Sum<Container>(
(Container a) => a.Items.Sum<Container>(
(Container b) => b.Value));
告诉编译器 lambda 表达式的目标类型完全可行:
_ = list.Sum(
a => a.Items.Sum(
(Func<Container, int>) (b => b.Value)));
或者您可以选择使用局部变量以不同的方式实现它:
var valueSelector = (Container b) => b.Value;
_ = list.Sum(
a => a.Items.Sum(valueSelector));
或者
Func<Container, int> valueSelector = b => b.Value;
_ = list.Sum(
a => a.Items.Sum(valueSelector));
(或者使用本地函数。)
所有这些基本上都使编译器的工作变得更轻松,从而可能缩短编译时间。如果编译时间对您来说不是问题,并且您不想更改代码,则可以隐藏该消息。显示该消息只是为了让编译器能够帮助解释为什么编译时间这么长。
|
–
–
–
–
–
|