使用 C# 方法组(如果可用)有什么好处吗?

发布于 2024-09-25 22:11:10 字数 264 浏览 5 评论 0原文

在处理诸如 List 之类的内容时,您可以编写以下内容:

list.ForEach(x => Console.WriteLine(x));

或者您可以使用方法组来执行相同的操作:

list.ForEach(Console.WriteLine);

我更喜欢第二行代码,因为它对我来说看起来更干净,但这有什么好处吗?

When dealing with something like a List<string> you can write the following:

list.ForEach(x => Console.WriteLine(x));

or you can use a method group to do the same operation:

list.ForEach(Console.WriteLine);

I prefer the second line of code because it looks cleaner to me, but are there any benefits to this?

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

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

发布评论

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

评论(7

赠意 2024-10-02 22:11:13

就我个人而言,我也更喜欢第二种,因为调试起来不那么混乱,但在这种情况下,我认为这只是风格问题,因为它们最终都完成了相同的事情。

Personally I also prefer the second because it's less confusing to debug, but in this case I think it's just a matter of style since they both end up getting the same thing done.

懒的傷心 2024-10-02 22:11:13

除了让喜欢方法组的人更愉快、让不喜欢方法组的人烦恼之外,没有任何实际好处。此外,它还会使您的代码与早期编译器不兼容。

-奥辛

No tangible benefits other than making it more pleasant to people who like method groups, and annoy people who dislike them [should that please you.] Also, it makes your code incompatible with earlier compilers.

-Oisin

桃扇骨 2024-10-02 22:11:12

正如其他人所指出的,存在由 lambda 引起的额外不必要的间接层。然而,也存在细微的语言差异。例如,在 C# 3 中,尝试执行返回类型推断时,泛型类型推断在 M(F) 上的工作方式与在 M(x=>F(x)) 上的工作方式不同。

有关详细信息,请参阅:

https://learn.microsoft.com/en-us/archive/blogs/ericlippert/c-3-0-return-type-in​​ference-does-not-work-on-方法组

和后续:

https://learn.microsoft.com/en-us/archive/blogs/ericlippert/method-type-in​​ference-changes-part-zero

As others have noted, there is an extra unnecessary layer of indirection induced by the lambda. However, there are subtle language differences as well. For example, in C# 3 generic type inference works differently on M(F) than on M(x=>F(x)) when attempting to perform return type inference.

For details see:

https://learn.microsoft.com/en-us/archive/blogs/ericlippert/c-3-0-return-type-inference-does-not-work-on-method-groups

and the follow-up:

https://learn.microsoft.com/en-us/archive/blogs/ericlippert/method-type-inference-changes-part-zero

浮世清欢 2024-10-02 22:11:12

我相信这是有好处的。在第一种情况下,您正在创建调用 Console.Writeline(string) 函数的匿名方法,而在另一种情况下,您只是将引用传递给现有函数。

I believe that there is a benefit. In first case you are creating anonymous method which calls Console.Writeline(string) function while in the other case you are just passing the reference to existing function.

小忆控 2024-10-02 22:11:12

是的;第一个实际上可能会导致不必要的额外临时调用发生;将 x 传递给一个简单调用 Console.WriteLine(x); 的方法您不需要执行第一个操作,因为 Console.WriteLine 已经是一个匹配的方法ForEach 正在寻找的签名。

Yes; the first actually can cause an unnecessary extra, interim call to happen; passing x in to a method that simply calls Console.WriteLine(x); You don't need to do the first one because Console.WriteLine already is a method which matches the signature that ForEach is looking for.

无敌元气妹 2024-10-02 22:11:11

好吧,让我们看看会发生什么。

static void MethodGroup()
{
    new List<string>().ForEach(Console.WriteLine);
}

static void LambdaExpression()
{
    new List<string>().ForEach(x => Console.WriteLine(x));
}

这将被编译成以下 IL。

.method private hidebysig static void MethodGroup() cil managed
{
    .maxstack 8
    L_0000: newobj instance void [mscorlib]System.Collections.Generic.List`1<string>::.ctor()
    L_0005: ldnull 
    L_0006: ldftn void [mscorlib]System.Console::WriteLine(string)
    L_000c: newobj instance void [mscorlib]System.Action`1<string>::.ctor(object, native int)
    L_0011: call instance void [mscorlib]System.Collections.Generic.List`1<string>::ForEach(class [mscorlib]System.Action`1<!0>)
    L_0016: ret 
}

.method private hidebysig static void LambdaExpression() cil managed
{
    .maxstack 8
    L_0000: newobj instance void [mscorlib]System.Collections.Generic.List`1<string>::.ctor()
    L_0005: ldsfld class [mscorlib]System.Action`1<string> Sandbox.Program::CS
lt;>9__CachedAnonymousMethodDelegate1
    L_000a: brtrue.s L_001d
    L_000c: ldnull 
    L_000d: ldftn void Sandbox.Program::<LambdaExpression>b__0(string)
    L_0013: newobj instance void [mscorlib]System.Action`1<string>::.ctor(object, native int)
    L_0018: stsfld class [mscorlib]System.Action`1<string> Sandbox.Program::CS
lt;>9__CachedAnonymousMethodDelegate1
    L_001d: ldsfld class [mscorlib]System.Action`1<string> Sandbox.Program::CS
lt;>9__CachedAnonymousMethodDelegate1
    L_0022: call instance void [mscorlib]System.Collections.Generic.List`1<string>::ForEach(class [mscorlib]System.Action`1<!0>)
    L_0027: ret 
}

请注意方法组方法如何创建一次性使用的 Action 委托,而 lambda 表达式方法如何创建隐藏的匿名委托字段并在必要时对其进行内联初始化。请注意 IL_000a 处的 brtrue 指令。

Well, let's take a look and see what happens.

static void MethodGroup()
{
    new List<string>().ForEach(Console.WriteLine);
}

static void LambdaExpression()
{
    new List<string>().ForEach(x => Console.WriteLine(x));
}

This gets compiled into the following IL.

.method private hidebysig static void MethodGroup() cil managed
{
    .maxstack 8
    L_0000: newobj instance void [mscorlib]System.Collections.Generic.List`1<string>::.ctor()
    L_0005: ldnull 
    L_0006: ldftn void [mscorlib]System.Console::WriteLine(string)
    L_000c: newobj instance void [mscorlib]System.Action`1<string>::.ctor(object, native int)
    L_0011: call instance void [mscorlib]System.Collections.Generic.List`1<string>::ForEach(class [mscorlib]System.Action`1<!0>)
    L_0016: ret 
}

.method private hidebysig static void LambdaExpression() cil managed
{
    .maxstack 8
    L_0000: newobj instance void [mscorlib]System.Collections.Generic.List`1<string>::.ctor()
    L_0005: ldsfld class [mscorlib]System.Action`1<string> Sandbox.Program::CS
lt;>9__CachedAnonymousMethodDelegate1
    L_000a: brtrue.s L_001d
    L_000c: ldnull 
    L_000d: ldftn void Sandbox.Program::<LambdaExpression>b__0(string)
    L_0013: newobj instance void [mscorlib]System.Action`1<string>::.ctor(object, native int)
    L_0018: stsfld class [mscorlib]System.Action`1<string> Sandbox.Program::CS
lt;>9__CachedAnonymousMethodDelegate1
    L_001d: ldsfld class [mscorlib]System.Action`1<string> Sandbox.Program::CS
lt;>9__CachedAnonymousMethodDelegate1
    L_0022: call instance void [mscorlib]System.Collections.Generic.List`1<string>::ForEach(class [mscorlib]System.Action`1<!0>)
    L_0027: ret 
}

Notice how the method group approach creates an Action<T> delegate for one time use and the lambda expression approach creates a hidden anonymous delegate field and does an inline initialization of it if necessary. Notice brtrue instruction at IL_000a.

清风挽心 2024-10-02 22:11:11

使用 lambda 表达式时存在额外的间接级别。对于这样的非闭包表达式,您只需在中间进行一个额外的方法调用,正如其他人提到的那样。

但还是有一些有趣的差异。在第二种情况下,每次调用时都会创建一个新的委托实例。对于前者,委托创建一次并缓存为隐藏字段,因此如果您调用很多,您将节省分配。

此外,如果您将局部变量引入 lambda 表达式,它就会变成一个闭包,并且将创建一个新类来保存此信息,而不仅仅是生成一个局部方法,这意味着在那里进行了额外的分配。

There is an extra level of indirection when using the lambda expression. With a non-closure expression like that, you'll simply have an extra method call in-between, as mentioned by others.

There are a few interesting differences though. In the second case, a new delegate instance is being created on each call. For the former, the delegate is created once and cached as a hidden field, so if you're calling a lot you'll save on allocations.

Additionally, if you introduce a local variable into the lambda expression, it becomes a closure and instead of just a local method being generated, a new class will be created to hold this information, meaning an extra allocation there.

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