我想我了解闭包是如何在 C# 和 .NET 中实现的。当 CLR 检测到一个函数将被传递到其作用域之外并作用于一个自由变量时,它会将函数和该变量打包到一个自定义类中。
那么这与object
调用它自己的函数之一时发生的情况有什么不同呢?本身作为第object
一个变量传递给函数,因此如果函数需要对象的任何其他函数、属性或字段,它可以访问它们。
我想我在这里遗漏了一些东西,并且想对这两种情况下发生的事情进行某种解释:闭包,以及对象调用其自己的方法之一的情况。
对象上的普通方法调用与闭包无关;如果您将该方法调用打包为委托,您只有一个闭包!
为了理解发生了什么,您需要很好地掌握“值类型”和“引用类型”的概念。在 .net 中谈论Object
不够明确,因为Object
它位于所有内容的层次结构的底部,包括值类型(int
也是Object
)。开始考虑对纯引用类型(如类实例)的方法调用要容易得多。
拿你的普通类变量和实例化:
List x; // variable declaration
x = new List(); // instantiation
或者,在一行中:
List x = new List();
您的实例的“主体”将保留在称为堆的内存区域中。您正在使用的x
仅包含对该内存区域的引用,这就是为什么将其称为reference type
.
List
当您使用变量调用方法时x
,该方法需要知道要在哪个列表上工作。因此,该方法只需获取对实例主体的引用作为第一个参数,即this
参数。说 CLR 或编译器传递“对象”是不正确的,因为对象总是在堆上,它只传递一个引用(或指针)。
当该方法需要调用同一对象的不同方法时,它只需传递与this
第一个参数相同的参数,即它自己接收到的参数。
正常的方法调用如下所示:
x.Add(Something); // calls instance method "Add" on "x"
当编译器看到它知道要x
用作调用的this
(第一个参数,隐藏的参数)的引用时Add
。那里没有“封闭”!
以下是闭包的样子:
List<int> A = new List<int>();
List<int> B = new List<int>();
Action<int> aDelegate; // <-- declare a delegate type variable
// Action<int> is a delegate that returns void and
// takes a single int parameter.
aDelegate = A.Add; // <-- initialize the delegate using an instance method of object A
aDelegate(7); // <- notice the call! No reference to "A" because "A" is already stored in aDelegate
aDelegate = B.Add;
aDelegate(8); // <- notice the call! No reference to "B" because "B" is already stored in aDelegate
如果您仔细查看调用委托 ( aDelegate(7)
) 时发生的情况,编译器不知何故需要调用Add
object 上的方法A
,但您看不到,它被隐藏了。委托封装了对对象的引用A
和方法的地址,Add
这就是为什么它被称为closure
. 作为对比,看看你这样做时会发生什么aDelegate(8)
;这个 time 方法Add
是在 object 上调用的B
,但无法猜测,因为对 object 的引用再次B
被埋在委托(闭包)中。