C#新语句后面的大括号是做什么用的?

71

给定下面的代码,position0的初始化方式和position1的初始化方式之间有什么区别?它们等效吗?如果不是,那么区别是什么?

class Program
{
    static void Main(string[] args)
    {
        Position position0 = new Position() { x=3, y=4 };

        Position position1 = new Position();
        position1.x = 3;
        position1.y = 4;
    }
}

struct Position
{
    public int x, y;
}
7个回答

67

它们并不完全等价 - 至少在一般情况下不是这样。使用对象初始化器的代码更接近于此:

Position tmp = new Position();
tmp.x = 3;
tmp.y = 4;
Position position1 = tmp;

换句话说,变量的赋值仅发生在属性被设置之后。现在,在声明新的本地变量的情况下,这实际上并不重要,编译器可能会优化到您的第一种形式。但从逻辑上讲,这是很重要的。考虑:

Position p1 = new Position { x = 10, y = 20 };

p1 = new Position { x = p1.y, y = p1.x };
如果先对p1进行赋值,你最终会得到p1.xp1.y都为0的结果。实际上,这与以下代码等价:
Position tmp = new Position();
tmp.x = 10;
tmp.y = 20;
Position p1 = tmp;

tmp = new Position();
tmp.x = p1.y; // 20
tmp.y = p1.x; // 10
p1 = tmp;

编辑: 我刚意识到你使用的是结构体而不是类。这可能会有一些微妙的差别...但你几乎肯定不应该从一开始就使用可变的结构体 :)


50

1
确实如此。我发布了我的答案...看到他的答案后,编辑了一下以指出他的答案。 - Joshua Rodgers
Jon Skeet 似乎回答了这个网站上的每一个问题 :) 我迫不及待地想读他的新书(如果它真的发布了的话)。 - Bryan

7
那是一个对象初始化器,允许你在单个表达式中分配值。最重要的是,这也适用于LINQ和匿名类型(否则为不可变类型)。还有一种类似的集合初始化器语法,用于向新集合添加项目。
请注意,有一个微妙的时间问题可能会很有用;使用初始化器时,所有赋值/添加都发生在变量被分配之前,这可以帮助阻止其他线程看到不完整的对象。否则,您需要一个额外的变量才能实现相同的结果。

Joshua和Slaks发帖称,Position初始化的两种方式产生了等效的IL代码。你是在说使用匿名类型时,我会得到不同的结果吗? - Vivian River
@rice - 是的:只有其中一个会编译 :) - Marc Gravell

2

它们是等效的,除了其中一个比另一个更易读。

还要考虑当您想将新对象传递给其他地方时的情况:

var aList = new List<Position>();
aList.Add( new Position() { x=3, y=4 } );

2

您提供的两个代码示例会生成相同的IL代码。(至少在发布版中是这样的)


2

忘记所有的IL内容,这只是一种速记符号。你正在做的是:

a. 在一个情况下,你明确地使用默认构造函数,然后设置两个属性。

b. 在另一个情况中,你使用新的初始化器语法,它隐式地让编译器执行了你在情况a中所做的操作。

尽管存在IL细微差别,但它们将为您实现相同的效果。


1

这两个是完全等效的。编译器实际上只是将第一个版本转换为第二个版本。

两者之间唯一的区别在于,使用第一个版本,您可以做很好的事情,例如将初始化版本传递给方法:

DoSomethingWithPoint(new Position() { x=3, y=4 });

这段代码比第二个初始化示例要多得多。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接