在函数调用中创建新对象

本文关键字:对象 新对象 函数调用 创建 | 更新日期: 2023-09-27 18:12:26

我想知道创建新对象作为方法(或构造函数调用)的一部分是否安全

例如下面这个痉挛的例子:

public class Igloo
{
    public int i = 5;
}
public class Program
{
    static void Main(string[] args)
    {
        DoSomething(new Igloo());
    }
    private static void DoSomething(Igloo i)
    {
        Debug.WriteLine(i.i);
    }
}

创建这样的临时对象是否不受欢迎?创建的新对象是否得到了正确的清理?

在函数调用中创建新对象

这没什么不对。

在发布模式下,为对象创建变量应该最终编译到相同的IL(免责声明:我没有检查过)。

如果您创建的是Windows窗体项目而不是控制台项目,您将在program.cs中发现以下行:

Application.Run(new FormMain());

所以似乎MS也做了完全相同的默认windows窗体项目

SLaks是对的——这是完全正确的,并且在所有意图和目的上都与以下操作相同:

Igloo igloo = new Igloo();
DoSomething(igloo);

在这两种情况下,一旦Igloo实例被传递给DoSomething方法-此时,该实例不再从Main方法中访问,使得该实例在DoSomething方法完成收集后具有收集资格。


如果我们想更详细地了解发生了什么,请查看IL:

.method private hidebysig static 
    void Main (string[] args) cil managed 
{
    // Method begins at RVA 0x2060
    // Code size 13 (0xd)
    .maxstack 8
    .entrypoint
    IL_0000: nop
    IL_0001: newobj instance void ConsoleApplication1.Igloo::.ctor()
    IL_0006: call void ConsoleApplication1.Program::DoSomething(class ConsoleApplication1.Igloo)
    IL_000b: nop
    IL_000c: ret
}

解释这是什么意思;我们看到,在IL_0001上,我们创建了Igloo的一个新实例——由于CLR是一个基于堆栈的虚拟机,该指令将对该实例的引用推入堆栈。下一条指令是对DoSomething的调用——CLR又是一个基于堆栈的虚拟机,所以参数从左到右被压入堆栈(因此堆栈上的最后一项是最右边的参数)。在这种情况下,只有1个参数,并且作为最后一次操作的结果已经在堆栈上,因此我们准备调用我们的方法。

如果我们将其与进行以下修改时产生的IL进行比较:

Igloo igloo = new Igloo();
DoSomething(igloo);

我们只能看到几个重要的区别:

.method private hidebysig static 
    void Main (string[] args) cil managed 
{
    // Method begins at RVA 0x2060
    // Code size 15 (0xf)
    .maxstack 1
    .entrypoint
    .locals init (
        [0] class ConsoleApplication1.Igloo igloo
    )
    IL_0000: nop
    IL_0001: newobj instance void ConsoleApplication1.Igloo::.ctor()
    IL_0006: stloc.0
    IL_0007: ldloc.0
    IL_0008: call void ConsoleApplication1.Program::DoSomething(class ConsoleApplication1.Igloo)
    IL_000d: nop
    IL_000e: ret
}

除了在方法的开始声明局部变量之外,唯一的区别是在创建Igloo和调用DoSomething之间调用stloc.0ldloc.0。这是什么?

stloc.0是一个将最后一项从堆栈中弹出并将其存储在第0个本地的指令。注意,在这一点上,我们不再有正确的堆栈来调用DoSomething(我们需要我们的Igloo实例位于堆栈的顶部),这就是ldloc.0的作用-它将第0个本地(返回)推到堆栈上。这两条指令就是我们的赋值指令。

请注意,当它被JITer编译成机器代码时,这两个语句几乎肯定会被优化出来。

这在c#中是安全且标准的,所有的垃圾收集器都会在这些对象不可访问时收集它们。

可以。该对象稍后将自动删除。