使用空对象初始化器有什么缺点吗?

8

我正在进行代码审查,发现我们的一位开发人员正在使用空对象初始化程序创建一个对象,如下所示:

List<Floorplan> floorplans = new List<Floorplan> { };

我不知道为什么他会这样实例化,而不是这样:
List<Floorplan> floorplans = new List<Floorplan>();

使用空对象初始化器来实例化对象有什么缺点吗?除了风格上不一致之外,我想知道是否有理由避免这种方法。有一些迹象表明它可能存在问题,但在发表意见之前,我想确认一下。


2
我认为var floorplans = new List<Floorplan>();是最好的。没有任何不利因素。 - Brad M
6
如果有疑问,检查一下IL代码。集合初始化器只是语法糖。 - User 12345678
2
FYI - ReSharper默认情况下会在空对象初始化器上引发警告。 - Austin Salonen
“{}” 是多余的,所以我建议使用“()”。如果您使用R#,我相信它会抱怨这个并提供从“{}”转换为“()”的选项。 - Christian Phillips
我检查了一下,Resharper 可以通过“查找用法”功能找到两个。 - 0lukasz0
5个回答

10
编译后的结果相同。
以下是 C#:
static void Main()
{
    var x = new List<int>();
    var y = new List<int> { };
}

编译成以下IL代码:

.method private hidebysig static 
    void Main () cil managed 
{
    // Method begins at RVA 0x2050
    // Code size 14 (0xe)
    .maxstack 1
    .entrypoint
    .locals init (
        [0] class [mscorlib]System.Collections.Generic.List`1<int32> x,
        [1] class [mscorlib]System.Collections.Generic.List`1<int32> y
    )

    IL_0000: nop
    IL_0001: newobj instance void class [mscorlib]System.Collections.Generic.List`1<int32>::.ctor()
    IL_0006: stloc.0
    IL_0007: newobj instance void class [mscorlib]System.Collections.Generic.List`1<int32>::.ctor()
    IL_000c: stloc.1
    IL_000d: ret
} // end of method Program::Main

如果您向集合中添加一个元素:

static void Main()
{
    var x = new List<int>();
    x.Add(1);
    var y = new List<int> { 1 };
}

这是生成的中间语言代码:

.method private hidebysig static 
    void Main () cil managed 
{
    // Method begins at RVA 0x2050
    // Code size 32 (0x20)
    .maxstack 2
    .entrypoint
    .locals init (
        [0] class [mscorlib]System.Collections.Generic.List`1<int32> x,
        [1] class [mscorlib]System.Collections.Generic.List`1<int32> y,
        [2] class [mscorlib]System.Collections.Generic.List`1<int32> '<>g__initLocal0'
    )

    IL_0000: nop
    IL_0001: newobj instance void class [mscorlib]System.Collections.Generic.List`1<int32>::.ctor()
    IL_0006: stloc.0
    IL_0007: ldloc.0
    IL_0008: ldc.i4.1
    IL_0009: callvirt instance void class [mscorlib]System.Collections.Generic.List`1<int32>::Add(!0)
    IL_000e: nop
    IL_000f: newobj instance void class [mscorlib]System.Collections.Generic.List`1<int32>::.ctor()
    IL_0014: stloc.2
    IL_0015: ldloc.2
    IL_0016: ldc.i4.1
    IL_0017: callvirt instance void class [mscorlib]System.Collections.Generic.List`1<int32>::Add(!0)
    IL_001c: nop
    IL_001d: ldloc.2
    IL_001e: stloc.1
    IL_001f: ret
} // end of method Program::Main

这展示了集合初始化器仅仅是语法糖。由于集合初始化器最初并不是C#的一部分,我认为人们更习惯于构造函数语法。如果我遇到使用空集合初始化器的代码,我会想知道其中缘由,但它绝对不会造成严重的可读性问题。如果一个人足够聪明以理解代码,那么对比 {}() 不应该让人感到沮丧,以致于削弱理解代码的能力。这归结为个人观点。做与您的团队达成共识的事情,如果只有您自己,那就尽情使用吧。


3
在这种情况下,没有任何缺点但要注意易读性。如果你在实例化时不需要额外的括号,则简单明了地不要使用它们。
我甚至会说更加注重生产力。这里的很多答案都试图深入回答这个问题。即使一种技术比另一种更好,我们也谈论的是一个微小的优化。

1

没有理由使用{ }初始化,所以我建议将其删除。开发者正在使用静态集合初始化器。唯一的原因是您可以使用一些项来启动集合,如果没有任何项,则没有意义。话虽如此,结果将是一个空集合,就像他正常调用构造函数一样。


但是内部容量会有所不同吗? - Esailija
@Esailija 这是个修辞问题吗?理论上可能存在这种情况,但基础数组长度不能为0,所以我会假设它会获得默认容量。 - evanmcdonnal
不,这只是一个问题,用来挑战那些过于快速地做出“它们确实是相同的”的假设。我真的不知道,但这可能是有可能的。 - Esailija
@Esailija 是的,那是一个很好的观点。在调试器中检查了这两个之后,我发现它们之间没有任何区别。所有暴露出来的值都完全相同。 - evanmcdonnal

1

它们基本相同,但使用默认构造函数更易读。


0

有很多评论说“默认构造函数更易读”。没有充分的理由认为(){}更(或更少)易读,因此您实际上正在进行一项意见调查。

如果您担心正确性,则同事的代码是可以的。如果您担心编码标准,则应确保将代码更改为符合您的标准(无论该标准是什么)。


没有好的理由认为()比{}更易读(或不易读)。我不确定这是真的。请记住,初始化语法只是在C# 3.0中引入的,因此有很多使用()语法的旧代码,并且()`语法是向后兼容的。这可能并不适用于每个人,但这是两个选择不仅仅是“意见调查”的原因。 - chollida
1
刚刚引入C# 3.0?这意味着这个特性已经有6年历史了。没有什么好理由不使用新功能,尤其是像这样“新”的功能。 - Andrew Coonce
我所指的是有些人被困在针对旧版本上。这只是生活中的一个事实。使用这个功能意味着你正式停止支持旧版本。这可能对你无关紧要,但每个开发者都应该意识到他们所做出的权衡。 - chollida

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