首页 > 解决方案 > 实例与方法作为参数——理解生命周期和垃圾收集

问题描述

在一个人为的例子中,我有一个支持以下接口的实例:

public interface ISelector<L, R>
{
    R Select(L value);
}

现在,我考虑两种调用方式IEnumerable.Select<L, R>(Func<L, R>)

IEnumerable<L> l;
ISelector<L, R> selector;
IEnumerable<R> r;
r = l.Select(v => selector.Select(v)); // 1
r = l.Select(selector.Select); // 2

方式 1 将变量对选择器的引用捕获到一个闭包中,这个闭包将独立于变量的范围持有这个引用selector。所以变量selector可能会超出范围,并且r.ToList()可能在变量selector超出范围后被调用。

但是,我不确定如何理解方式 2:如何ISelector<L, R>.Select分配和捕获?变量是否可能selector超出范围并可能r.ToList()在变量超出范围后被调用selector

可读性我更喜欢方式 2,但在使用它之前,我想先了解捕获和垃圾收集部分的生命周期。

标签: c#garbage-collection

解决方案


在第二个示例中,selector.Select被解释为新委托实例的简写,其中selector是目标并且ISelector<L, R>.SelectMethodInfo. 因此,selector可以通过委托实例访问,因此:只要委托是可访问的,则其对象selector是可访问的,并且不能被收集。所以是的,您可以随时ToList()安全地拨打电话。

具体来说,在这里,我们看到var projected l.Select(obj.Select);

// l. [push 1]
IL_000d: ldloc.1 
// obj. [push 1]
IL_000e: ldloc.0 
// Select [push 1]
IL_000f: ldftn instance string X::Select(int32) 
// new Func<int,string>([pop 2, push 1])
IL_0015: newobj instance void class [System.Private.CoreLib]System.Func`2<int32, string>::.ctor(object, native int)
// Enumerable.Select<int,string>([pop 2, push 1])
IL_001a: call class [System.Private.CoreLib]System.Collections.Generic.IEnumerable`1<!!1> [System.Linq]System.Linq.Enumerable::Select<int32, string>(class [System.Private.CoreLib]System.Collections.Generic.IEnumerable`1<!!0>, class [System.Private.CoreLib]System.Func`2<!!0, !!1>)
// projected=[pop 1]
IL_001f: stloc.2

这是代表


推荐阅读