【发布时间】:2015-07-21 05:14:48
【问题描述】:
我阅读了一篇关于 C# 和性能注意事项的文章 (here)
文章中说委托分配会触发内存分配,例如:
每一个局部变量赋值,例如“Func fn = Fn” 在堆上创建委托类 Func 的新实例
我想知道这是否属实,如果属实 - 它是如何实现的?我不熟悉引用分配可以在 C# 中触发额外内存分配的任何方式。
【问题讨论】:
标签: c# .net memory-management mono garbage-collection
我阅读了一篇关于 C# 和性能注意事项的文章 (here)
文章中说委托分配会触发内存分配,例如:
每一个局部变量赋值,例如“Func fn = Fn” 在堆上创建委托类 Func 的新实例
我想知道这是否属实,如果属实 - 它是如何实现的?我不熟悉引用分配可以在 C# 中触发额外内存分配的任何方式。
【问题讨论】:
标签: c# .net memory-management mono garbage-collection
文章是对的。很容易测试:
static void Main(string[] args)
{
Action<string[]> main = Main;
Action<string[]> main2 = Main;
Console.WriteLine(object.ReferenceEquals(main, main2)); // False
}
如果你查看生成的http://goo.gl/S47Wfy 的 IL 代码,很清楚会发生什么:
IL_0002: ldftn void Test::Main(string[])
IL_0008: newobj instance void class [mscorlib]System.Action`1<string[]>::.ctor(object, native int)
IL_000d: stloc.0
IL_000e: ldnull
IL_000f: ldftn void Test::Main(string[])
IL_0015: newobj instance void class [mscorlib]System.Action`1<string[]>::.ctor(object, native int)
IL_001a: stloc.1
所以有两个 newobj instance void class [mscorlib]System.Action1::.ctor(object, native int)`
请注意,您是对的,这是违反直觉的:
public class TestEvent
{
public event Action Event;
public TestEvent()
{
Action d1 = Print;
Action d2 = Print;
// The delegates are distinct
Console.WriteLine("d1 and d2 are the same: {0}", object.ReferenceEquals(d1, d2));
Event += d1;
Event -= d2;
// But the second one is able to remove the first one :-)
// (an event when is empty is null)
Console.WriteLine("d2 was enough to remove d1: {0}", Event == null);
}
public void Print()
{
Console.WriteLine("TestEvent");
}
}
以events 为例,您可以使用“等效但不相同”的委托来移除另一个委托,如示例所示。见https://ideone.com/xeJ6LO
【讨论】:
new C() 两次,您将获得C 的两个实例。如果您定义一个调用 new C() 的隐式转换运算符,并隐式执行该转换两次,您还将获得两个 C 实例。
new MyDelegate(function)。然后在 C# 2.0 中,他们将其隐式化。我要补充一点,即使该表单也是假的,因为委托的构造函数的签名是MyDelegate(object, IntPtr),其中object 是对委托的“目标”对象的引用(this 将是由委托方法使用),IntPtr 是方法的地址。你可以看看newobj在IL代码中是如何被调用的(见第二个代码块)
int i = 1; C c1 = i; C c2 = i;(没有任何显式new C()s),但也可以获得!object.ReferenceEquals(c1, c2)。事实上,即使隐式转换为内置类型也会发生这种情况:int i = 1; object o1 = i; object o2 = i; 现在object.ReferenceEquals(o1, o2) 报告了什么?
显然,您正在声明一个新的委托实例并将其初始化:
Func fn=new Func(Fn); // hidden by syntactic sugar
但是这篇文章在更高的内存使用方面具有误导性,它和其他文章一样,只是从来没有被垃圾收集,因为它的数量太少了。
【讨论】:
Fn 是一个函数。