实例与方法作为参数——理解生命周期和垃圾回收
instance vs. method as argument -- understanding lifetime and garbage collection
在一个人为的例子中,我有一个支持以下接口的实例:
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,但在使用它之前,我想先了解捕获和垃圾收集部分的生命周期。
在第二个示例中,selector.Select
被解释为新委托实例的 shorthand,其中 selector
是目标,ISelector<L, R>.Select
是 MethodInfo
.因此,selector
是通过委托实例可达的,因此:只要委托可达,selector
的对象就是可达的,并且不能被收集。所以是的,您可以随时安全地调用 ToList()
。
具体来说,here,我们看到 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
哪个是代表
在一个人为的例子中,我有一个支持以下接口的实例:
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,但在使用它之前,我想先了解捕获和垃圾收集部分的生命周期。
在第二个示例中,selector.Select
被解释为新委托实例的 shorthand,其中 selector
是目标,ISelector<L, R>.Select
是 MethodInfo
.因此,selector
是通过委托实例可达的,因此:只要委托可达,selector
的对象就是可达的,并且不能被收集。所以是的,您可以随时安全地调用 ToList()
。
具体来说,here,我们看到 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
哪个是代表