FunctionGHW

立志成为真正程序员的码农,伪全栈,目前主要写C#

C#对象初始化器语法小深入探讨

FunctionGHW posted @ 2014年3月14日 15:06 in C#与.Net with tags C# Object Initializers 对象初始化器 , 1713 阅读

C#3.0提供了一个新的语法,对象初始化器(Object Initializers)。 该语法是为了简化一个常见的编程模式:构造一个新对象,逐个设置对象的公共属性。

初探

实例如下:

class Program
{
    static void Main(string[] args)
    {   //对象初始化器
        Person p = new Person() {
            FirstName = "Hello",
            LastName = "World"
        };
        Console.WriteLine(p);
        Console.ReadKey();
    }
}

class Person
{
    public string FirstName { get;set; }
    public string LastName { get;set; }

    public override string ToString()
    {
        return string.Format("{0} {1}", FirstName, LastName);
    }
}
        

从一开始我就知道这个语法等价于下面的语句:

Person p = new Person();
p.FirstName = "Hello";
p.LastName = "World";
...
        

我自然也就认为编译器也会用这样的代码进行替换。偶然一次起了好奇心, 想看看编译器是不是真的这么做的,于是就查看生成的IL,结果真发现了点东西。IL代码我只能看个大概, 下面是在Reflector后的结果(选项设置为C#, .net2.0):

    Person <>g__initLocal0 = new Person();
    <>g__initLocal0.FirstName = "Hello";
    <>g__initLocal0.LastName = "World";
    Person p = <>g__initLocal0;
    Console.WriteLine(p);
    Console.ReadKey();
        

多了个局部变量了<>g__initLocal0, 构建的对象完成所有属性设置后再赋值给p。虽然我不理解为什么要加一个"临时变量", 但是这不会影响代码的最终结果。

再探

原本到此就该结束了,但是在好奇心的驱使下又做了一个新的测试。 我把Person的定义改成了struct,这样他就是个值类型了。对象初始化器的语法对值类型依然有效,Reflector的结果没有变化, 但是这个赋值语句的意义就不一样了:

  • 如果Person是引用类型,那么此处仅仅是引用的复制。
  • 如果Person是值类型,那么这里就是完整的复制了<>g__initLocal0
再仔细想想,我觉得还有两点区别:
  • 引用的复制应该比值拷贝要快,如果这个值类型有n多个字段的话,这个差别就很大。
  • 值类型的局部变量整个对象在线程栈上,占用更多线程栈的内存,包含的字段越多占用就越多。
这样就有一个结论:对值类型使用对象初始化语法,要付出比引用类型更高的代价。 当然了,除非这个值类型真的有太多的公共属性或者该语法用在一个执行超过几万次的循环里, 否则这点代价是可以忽略不计的。

 

 


登录 *


loading captcha image...
(输入验证码)
or Ctrl+Enter