返回新类型和返回对象之间有区别吗?

时间:2013-06-28 07:33:30

标签: c# coding-style

在C#中,为两种不同的写作方式生成的字节代码有什么不同,我期望是同样的事情:

返回创建的对象:

public MemoryStream GetStream() {
  MemoryStream s = new MemoryStream(this.GetBytes());
  return s;
}

返回新:

public MemoryStream GetStream() {
  return new MemoryStream(this.GetBytes());
}

是否会优化差异?或者第一个比第二个更容易受垃圾收集影响?或者这只是个人偏好?

4 个答案:

答案 0 :(得分:1)

查看IL代码,似乎第二个版本中的步骤少于第一个版本。

.method public hidebysig 
    instance class [mscorlib]System.IO.MemoryStream GetStream1 () cil managed 
{
    // Method begins at RVA 0x22c0
    // Code size 13 (0xd)
    .maxstack 1
    .locals init (
        [0] class [mscorlib]System.IO.MemoryStream s,
        [1] class [mscorlib]System.IO.MemoryStream CS$1$0000
    )

    IL_0000: nop
    IL_0001: newobj instance void [mscorlib]System.IO.MemoryStream::.ctor()
    IL_0006: stloc.0
    IL_0007: ldloc.0
    IL_0008: stloc.1
    IL_0009: br.s IL_000b

    IL_000b: ldloc.1
    IL_000c: ret
} // end of method Form1::GetStream1

.method public hidebysig 
    instance class [mscorlib]System.IO.MemoryStream GetStream2 () cil managed 
{
    // Method begins at RVA 0x22dc
    // Code size 11 (0xb)
    .maxstack 1
    .locals init (
        [0] class [mscorlib]System.IO.MemoryStream CS$1$0000
    )

    IL_0000: nop
    IL_0001: newobj instance void [mscorlib]System.IO.MemoryStream::.ctor()
    IL_0006: stloc.0
    IL_0007: br.s IL_0009

    IL_0009: ldloc.0
    IL_000a: ret
} // end of method Form1::GetStream2

它看起来似乎没有更多的地狱,但还有更多的步骤。

@Alexei Levenkov,这是代码的发布版本

.method public hidebysig 
    instance class [mscorlib]System.IO.MemoryStream GetStream1 () cil managed 
{
    // Method begins at RVA 0x2264
    // Code size 8 (0x8)
    .maxstack 1
    .locals init (
        [0] class [mscorlib]System.IO.MemoryStream s
    )

    IL_0000: newobj instance void [mscorlib]System.IO.MemoryStream::.ctor()
    IL_0005: stloc.0
    IL_0006: ldloc.0
    IL_0007: ret
} // end of method Form1::GetStream1

.method public hidebysig 
    instance class [mscorlib]System.IO.MemoryStream GetStream2 () cil managed 
{
    // Method begins at RVA 0x2278
    // Code size 6 (0x6)
    .maxstack 8

    IL_0000: newobj instance void [mscorlib]System.IO.MemoryStream::.ctor()
    IL_0005: ret
} // end of method Form1::GetStream2

似乎还有点多。

答案 1 :(得分:1)

如果您使用Reflector检查为此生成的代码:

public MemoryStream GetStream(byte[] bytes)
{
    MemoryStream s = new MemoryStream(bytes);
    return s;
}

对于发布版本,您可以获得:

.method public hidebysig instance class [mscorlib]System.IO.MemoryStream GetStream(uint8[] bytes) cil managed
{
    .maxstack 1
    .locals init (
        [0] class [mscorlib]System.IO.MemoryStream s)
    L_0000: ldarg.1 
    L_0001: newobj instance void [mscorlib]System.IO.MemoryStream::.ctor(uint8[])
    L_0006: stloc.0 
    L_0007: ldloc.0 
    L_0008: ret 
}

正如您所看到的,C#编译器已经优化了额外的变量。

但是,对于调试版本,您可以得到:

.method public hidebysig instance class [mscorlib]System.IO.MemoryStream GetStream(uint8[] bytes) cil managed
{
    .maxstack 1
    .locals init (
        [0] class [mscorlib]System.IO.MemoryStream s,
        [1] class [mscorlib]System.IO.MemoryStream CS$1$0000)
    L_0000: nop 
    L_0001: ldarg.1 
    L_0002: newobj instance void [mscorlib]System.IO.MemoryStream::.ctor(uint8[])
    L_0007: stloc.0 
    L_0008: ldloc.0 
    L_0009: stloc.1 
    L_000a: br L_000f
    L_000f: ldloc.1 
    L_0010: ret 
}

显然编译器无法优化调试构建的额外变量,以防您在调试时检查它。

因此,如果您想将额外的变量保留用于调试目的,那么它很好 - 它对发布版本没有任何影响。

答案 2 :(得分:1)

我相信结果优化的JITed代码将是相同的。

对GC的行为肯定没有影响,因为对象的生命周期将取决于使用返回值的人(你可能会考虑在函数结束之前不再使用值的情况,这显然不是这里的情况 - {{ 1}}在方法执行结束时返回。)

非优化(调试)构建中唯一明显的区别是,您将能够看到s变量的值。

答案 3 :(得分:0)

这两个代码段 主要 相同,性能差异很小,可以忽略不计,从代码风格和方法功能的角度来看,它们是可以选择的。 如果你的方法除了返回MemoryStream对象之外不应该做任何其他事情,那么第二个代码片段就足够了,但如果你需要在返回它之前对MemoryStream对象执行某些操作,必须使用第一个。 垃圾收集方面没有区别。