返回新类型和返回对象之间有区别吗
本文关键字:返回 对象 之间 有区别 新类型 类型 | 更新日期: 2023-09-27 18:20:25
在C#中,为两种不同的编写方式生成的字节码有什么不同吗?我希望是一样的:
返回创建对象:
public MemoryStream GetStream() {
MemoryStream s = new MemoryStream(this.GetBytes());
return s;
}
新退货:
public MemoryStream GetStream() {
return new MemoryStream(this.GetBytes());
}
会优化掉任何差异吗?还是第一个比第二个更容易受到垃圾收集的影响?还是这只是个人喜好?
查看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
看起来还是稍微多一点。
如果您使用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
}
显然,编译器无法优化掉用于调试构建的额外变量,以防您在调试时检查它。
因此,如果您想保留额外的变量用于调试目的,那么这很好——它对发布版本没有影响。
我相信最终优化的JITed代码将是相同的。
这肯定不会对GC行为产生影响,因为对象的生存期将由使用返回值的人决定(您可能会想到在函数结束前不再使用值的情况,但这里显然不是这样——s
在方法执行结束时返回)。
在非优化(调试)构建中,您将能够看到s
变量的值。
这两个代码片段大部分相同,性能差异很小,可以忽略不计,从它们中进行选择取决于代码样式和方法功能。如果您的方法除了返回MemoryStream
对象之外不应该做任何其他事情,那么第二个代码片段就足够了,但如果您需要在返回MemoryStream
对象之前对其执行一些操作,则必须使用第一个。在垃圾回收方面没有区别。