使用对象初始值设定项时,为什么编译器会生成额外的局部变量?

2024-01-06

昨天在回答有关 SO 的问题时,我注意到如果使用对象初始化程序初始化对象,编译器会创建一个额外的局部变量。

考虑以下 C# 3.0 代码,在 VS2008 中以发布模式编译:

public class Class1
{
    public string Foo { get; set; }
}

public class Class2
{
    public string Foo { get; set; }
}

public class TestHarness
{
    static void Main(string[] args)
    {
        Class1 class1 = new Class1();
        class1.Foo = "fooBar";

        Class2 class2 =
            new Class2
            {
                Foo = "fooBar2"
            };

        Console.WriteLine(class1.Foo);
        Console.WriteLine(class2.Foo);
    }
}

使用 Reflector,我们可以检查 Main 方法的代码:

.method private hidebysig static void Main(string[] args) cil managed
{
    .entrypoint
    .maxstack 2
    .locals init (
        [0] class ClassLibrary1.Class1 class1,
        [1] class ClassLibrary1.Class2 class2,
        [2] class ClassLibrary1.Class2 <>g__initLocal0)
    L_0000: newobj instance void ClassLibrary1.Class1::.ctor()
    L_0005: stloc.0 
    L_0006: ldloc.0 
    L_0007: ldstr "fooBar"
    L_000c: callvirt instance void ClassLibrary1.Class1::set_Foo(string)
    L_0011: newobj instance void ClassLibrary1.Class2::.ctor()
    L_0016: stloc.2 
    L_0017: ldloc.2 
    L_0018: ldstr "fooBar2"
    L_001d: callvirt instance void ClassLibrary1.Class2::set_Foo(string)
    L_0022: ldloc.2 
    L_0023: stloc.1 
    L_0024: ldloc.0 
    L_0025: callvirt instance string ClassLibrary1.Class1::get_Foo()
    L_002a: call void [mscorlib]System.Console::WriteLine(string)
    L_002f: ldloc.1 
    L_0030: callvirt instance string ClassLibrary1.Class2::get_Foo()
    L_0035: call void [mscorlib]System.Console::WriteLine(string)
    L_003a: ret 
}

在这里,我们可以看到编译器生成了两个对实例的引用Class2 (class2 and <>g__initLocal0),但只有一个对实例的引用Class1 (class1).

现在,我对 IL 不太熟悉,但看起来它正在实例化<>g__initLocal0, 设置前class2 = <>g__initLocal0.

为什么会发生这种情况?

那么,使用对象初始值设定项时是否会产生性能开销(即使非常轻微)?


线程安全和原子性。

首先,考虑这行代码:

MyObject foo = new MyObject { Name = "foo", Value = 42 };

任何阅读该声明的人都可能合理地假设foo对象将是原子的。在赋值之前,该对象根本不存在。分配完成后,对象就存在并处于预期状态。

现在考虑翻译该代码的两种可能方法:

// #1
MyObject foo = new MyObject();
foo.Name = "foo";
foo.Value = 42;

// #2
MyObject temp = new MyObject();  // temp will be a compiler-generated name
temp.Name = "foo";
temp.Value = 42;
MyObject foo = temp;

在第一种情况下foo对象在第一行实例化,但直到最后一行执行完毕后才会处于预期状态。如果另一个线程在最后一行执行之前尝试访问该对象,会发生什么情况?该对象将处于半初始化状态。

在第二种情况下foo对象直到最后一行分配时才存在temp。由于引用赋值是一个原子操作,因此它给出的语义与原始的单行赋值语句完全相同。即,foo对象永远不会以半初始化状态存在。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

使用对象初始值设定项时,为什么编译器会生成额外的局部变量? 的相关文章

  • 在Application_AquireRequestState事件中用POST数据重写Url

    我有一个在其中注册路线的代码Application AcquireRequestState应用程序的事件 注册路由后 我会在 Http 运行时缓存中设置一个标志 这样我就不会再次执行路由注册代码 在此事件中注册路线有特定原因Applicat
  • 用户控件内所有控件均为空

    我有一个 UserControl 它使用 UserControl 以及其他控件 In the ascx文件我有以下代码
  • 找到的程序集的清单定义与程序集引用不匹配

    我试图在 C Windows 窗体应用程序 Visual Studio 2005 中运行一些单元测试 但出现以下错误 System IO FileLoadException 无法加载文件或程序集 实用程序 版本 1 2 0 200 文化 中
  • 无法从 Web api POST 读取正文数据

    我正在尝试从新的 Asp Net Web Api 中的请求中提取一些数据 我有一个像这样的处理程序设置 public class MyTestHandler DelegatingHandler protected override Syst
  • 如何在编译C代码时禁用警告?

    我正在使用 32 位 Fedora 14 系统 我正在使用编译我的源代码gcc 有谁知道如何在编译c代码时禁用警告 EDIT 是的 我知道 最好的办法是修复这些警告以避免任何未定义 未知的行为 但目前在这里 我第一次编写了巨大的代码 并且在
  • C# datagridview 列转入数组

    我正在用 C 构建一个程序 并在其中包含一个 datagridview 组件 datagridview 有固定数量的列 2 我想将其保存到两个单独的数组中 但行数确实发生了变化 我怎么能这样做呢 假设一个名为 dataGridView1 的
  • 无法加载文件或程序集“EntityFramework,版本=6.0.0.0”

    我究竟做错了什么 我该如何解决这个问题 我有一个包含多个项目的解决方案 它是一个 MVC NET 4 5 Web 应用程序 在调试模式下启动后调用其中一个项目时 出现此错误 导致此错误的项目具有以下参考 两个都是版本6 0 0 0 应用程序
  • 格式化货币

    在下面的示例中 逗号是小数点分隔符 我有这个 125456 89 我想要这个 125 456 89 其他示例 23456789 89 gt 23 456 789 89 Thanks 看看这个例子 double value 12345 678
  • C#中Enum中定义的value__是什么

    What value 可能在这里 value MSN ICQ YahooChat GoogleTalk 我运行的代码很简单 namespace EnumReflection enum Messengers MSN ICQ YahooChat
  • 在简单注入器中注册具有多个构造函数和字符串依赖项的类型

    我正在尝试弄清楚如何使用 Simple Injector 我在项目中使用了它 注册简单服务及其组件没有任何问题 但是 当组件具有两个以上实现接口的构造函数时 我想使用依赖注入器 public DAL IDAL private Logger
  • 系统错误 124 - SHFileOperation 的 ERROR_INVALID_LEVEL

    我在使用时遇到问题SHFileOperation SHFileOperation SHFILEOPSTRUCT https stackoverflow com questions 9191415 shfileoperation shfile
  • 不兼容的类型 - 是因为数组已经是指针吗?

    在下面的代码中 我创建一个基于书籍结构的对象 并让它保存多个 书籍 我设置的是一个数组 即定义 启动的对象 然而 每当我去测试我对指针的了解 实践有帮助 并尝试创建一个指向创建的对象的指针时 它都会给我错误 C Users Justin D
  • .NET JIT 编译的代码缓存在哪里?

    NET 程序首先被编译为 MSIL 代码 当它被执行时 JIT编译器会将其编译为本机机器代码 我想知道 这些JIT编译的机器代码存储在哪里 它只存储在进程的地址空间中吗 但由于程序的第二次启动比第一次快得多 我认为即使在执行完成后 该本机代
  • 使用(linq to sql)更新错误

    我有两个表 通过外键 CarrierID 绑定 Carrier CarrierID CarrierName CarrierID 1 CarrierName DHL CarrierID 2 CarrierName Fedex Vendor V
  • TPL 数据流块下游如何获取源生成的数据?

    我正在使用 TPL Dataflow 处理图像 我收到处理请求 从流中读取图像 应用多次转换 然后将生成的图像写入另一个流 Request gt Stream gt Image gt Image gt Stream 为此 我使用块 Buff
  • 如何将对象转换为传递给函数的类型?

    这不会编译 但我想做的只是将对象转换为传递给函数的 t public void My Func Object input Type t t object ab TypeDescriptor GetConverter t ConvertFro
  • C++ [Windows] 可执行文件所在文件夹的路径[重复]

    这个问题在这里已经有答案了 我需要访问一些文件fstream在我的 Windows 上的 C 应用程序中 这些文件都位于我的exe文件所在文件夹的子文件夹中 获取当前可执行文件的文件夹路径的最简单且更重要的 最安全的方法是什么 Use 获取
  • 使用 System.Security.Cryptography 将 Win32 Crypto API 调用转换为 C#

    我接到一项任务 要删除我们产品的一个 dll 并将其替换为纯 C 的 dll 旧的 DLL 是 NET 2 0 托管 C C CLI 它包装了对 Win32 本机加密 API 的调用 新的 DLL 应公开具有相同名称和方法的新对象 但应使用
  • C# 粘贴到文本框时检查剪贴板中的字符

    有没有一些方法可以在粘贴到文本框 C 之前仅检查剪贴板中的字符 Ctrl V 和右键单击 gt 粘贴 但不使用 MaskedTextbox 在文本框文本更改中添加规则以仅接受数字 例如 private string value privat
  • 无法使 Polly 超时策略覆盖 HttpClient 默认超时

    我正在使用 Polly 重试策略 并且正如预期的那样 在重试过程中HttpClient达到 100 秒超时 我尝试了几种不同的方法来合并 Polly 超时策略 将超时移至每次重试而不是总计 但 100 秒超时仍然会触发 我读过大约 5 个

随机推荐