【问题标题】:What is the difference between Expression bodied syntax vs Getter syntax on IL level?IL级别的表达式正文语法与Getter语法有什么区别?
【发布时间】:2026-01-06 02:10:01
【问题描述】:

我想稍微了解一下IL代码。

因此,正如您所见,表达式 bodied 的代码比 GetOld 代码少。是否在那里进行了一些优化并意味着表达式主体语法更高效?

还是没关系?

namespace DatabaseModules {
    public class Test {
        public IList<string> _cache = new List<string>();

        public Test() {

        }

        public IList<string> Get => _cache;

        public IList<string> GetOld {
            get { return _cache; }
        }
    }
}

以及使用 DotPeek 生成的 IL 代码

https://gist.github.com/anonymous/9673389a1a21d0ad8122ec97178cfd9a

【问题讨论】:

标签: c# cil


【解决方案1】:

When compiling in Release mode, both properties produce the same IL:

.method public hidebysig specialname 
    instance class [mscorlib]System.Collections.Generic.IList`1<string> get_Get () cil managed 
{
    // Method begins at RVA 0x2063
    // Code size 7 (0x7)
    .maxstack 8

    IL_0000: ldarg.0
    IL_0001: ldfld class [mscorlib]System.Collections.Generic.IList`1<string> DatabaseModules.Test::_cache
    IL_0006: ret
} // end of method Test::get_Get

.method public hidebysig specialname 
    instance class [mscorlib]System.Collections.Generic.IList`1<string> get_GetOld () cil managed 
{
    // Method begins at RVA 0x2063
    // Code size 7 (0x7)
    .maxstack 8

    IL_0000: ldarg.0
    IL_0001: ldfld class [mscorlib]System.Collections.Generic.IList`1<string> DatabaseModules.Test::_cache
    IL_0006: ret
} // end of method Test::get_GetOld

When compiling in Debug mode, the IL is indeed different:

.method public hidebysig specialname 
    instance class [mscorlib]System.Collections.Generic.IList`1<string> get_Get () cil managed 
{
    // Method begins at RVA 0x2065
    // Code size 7 (0x7)
    .maxstack 8

    IL_0000: ldarg.0
    IL_0001: ldfld class [mscorlib]System.Collections.Generic.IList`1<string> DatabaseModules.Test::_cache
    IL_0006: ret
} // end of method Test::get_Get

.method public hidebysig specialname 
    instance class [mscorlib]System.Collections.Generic.IList`1<string> get_GetOld () cil managed 
{
    // Method begins at RVA 0x2070
    // Code size 12 (0xc)
    .maxstack 1
    .locals init (
        [0] class [mscorlib]System.Collections.Generic.IList`1<string>
    )

    IL_0000: nop
    IL_0001: ldarg.0
    IL_0002: ldfld class [mscorlib]System.Collections.Generic.IList`1<string> DatabaseModules.Test::_cache
    IL_0007: stloc.0
    IL_0008: br.s IL_000a
    IL_000a: ldloc.0
    IL_000b: ret
} // end of method Test::get_GetOld

添加的说明是:

  1. nop 指令,其目的是什么都不做。
  2. 一条br指令,跳转到下一条指令;实际上,这没有任何作用。
  3. stloc-ldloc 指令对,存储然后从局部变量加载值,实际上什么都不做。

所以,添加的指令不会做任何事情。我相信它们中的大多数都是为了帮助调试(即nop 在那里,以便您可以在左大括号上放置一个断点;这由您的 dotPeek 输出中的注释表示)。其中一些可能是编译器内部工作方式的产物,在调试模式下不会被删除,因为没有理由这样做。

最后,差异并不重要。并且它不会在 Release 模式下造成性能差异,因为该模式下的 IL 没有差异。

【讨论】:

  • Br_S & Ldloc_0 :调试构建中的这些附加说明是允许在最后具有} 的方法上进行操作,以及像您可以使用 VS 验证一样监视返回的结果调试器。使用 EB 无需步骤或间谍,因此无需额外代码。
【解决方案2】:

没有。该代码compiles to the exact same C# 使用 Roslyn,因此 IL 也不例外:

using System.Runtime.CompilerServices;
using System.Security;
using System.Security.Permissions;

[assembly: AssemblyVersion("0.0.0.0")]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[module: UnverifiableCode]
namespace DatabaseModules
{
    public class Test
    {
        public IList<string> _cache = new List<string>();

        public IList<string> Get
        {
            get
            {
                return this._cache;
            }
        }

        public IList<string> GetOld
        {
            get
            {
                return this._cache;
            }
        }
    }
}

【讨论】:

  • 嗯,这个转换实际上在做什么?我真的不明白
  • 这是由 Roslyn 编译器评估的代码。表达式主体成员与“旧”吸气剂没有什么不同。
  • 每当我们编译时,流程是否总是发生,该类由 Roslyn 生成,然后编译为 IL?
  • Roslyn 解析您的代码并从中生成 IL。作为中间步骤,它会创建您可以返回的表达式,就像上面的代码一样。
  • @Sherry - 在编译期间,C# 中的一些功能被“降低”了 - 变成了更简单/更旧的代码。此特定功能可以在 C# 编译器中实现,而无需对 CLR 进行任何更改,因为它可以以这种方式实现。甚至例如foreach 在编译器转换为 IL 时不存在 - 这是另一个降低到不同代码的功能。
最近更新 更多