假设我们在。net 5中有这个
using System;
public class ClassWithMethod
{
public void MethodToCall()
{
Console.WriteLine("hi");
}
}
public class Program
{
private ClassWithMethod instance = new ClassWithMethod();
private void Call(Action action)
{
action();
}
public void DoStuff()
{
Call(instance.MethodToCall);
}
public static void Main()
{
Program program = new Program();
program.DoStuff();
}
}
我保证Call(instance.MethodToCall);
不会在我背后创建任何对象吗?如果我要调用这个方法上百万次,GC就不能在这里运行。
显然Program program = new Program();
和private ClassWithMethod instance = new ClassWithMethod();
创建了两个新对象,但是没有一些第三个被引入存在,如运行时插入一些匿名函数未经我的许可?在,有没有额外的对象创建发生在DoStuff()
方法内部?
注意这是一个严格的要求。我不能产生任何垃圾,否则会发生不好的事情。
医生说
委托类似于c++函数指针,但委托是完全面向对象的,并且与c++指向成员函数的指针不同,委托封装了对象实例和方法。
如果方法和对象被传递下去是可以的,但是我不能在这样做的过程中创建一个完整的新对象。
我相信这是传递给delegate Action的,但是我找不到任何文档保证不会发生导致某个对象出现并在以后的道路上咬我的魔法。
例如,我曾经将lambda传递给函数,我希望VM能够内联它们,并通过对我来说聪明的方式完全避免对象创建,但它没有,然后我后来为此付出了可怕的代价,不得不重写代码。这是我的错,因为这是我使用lambdas的方式所期望的,但是VM在过去的优化方面非常出色,所以我认为它已经做了疯狂的黑魔法优化,它可以完全为我忽略对象,我完全责怪自己,因为我不应该做出这样的假设,但这是激发这个问题的痛点。
您可以通过使用Linqpad或ILSpy等工具检查生成的IL来轻松地自己测试这一点。
Inside of Program。DoStuff,为Action构造函数创建一个分配:
IL_0000 nop
IL_0001 ldarg.0
IL_0002 ldarg.0
IL_0003 ldfld Program.instance
IL_0008 ldftn ClassWithMethod.MethodToCall ()
IL_000E newobj Action..ctor
IL_0013 call Program.Call (Action)
IL_0018 nop
IL_0019 ret
是的,这将为DoStuff方法内部的动作生成一个对象。