C#/.NET 匿名函数会捕获变量,并延长对象的生命周期
小伙伴在一次垃圾回收中,发现对象并没有被回收掉,而注释掉一句代码后它便能够回收。
这究竟是为什么?
不关心探索过程的就直接拉到最后看结论吧!
探索
测试代码是这样的:
private void OnLoaded(object sender, RoutedEventArgs e)
{
var variable = new MainPage();
var reference = new WeakReference<MainPage>(variable);
variable = null;
GC.Collect();
Console.WriteLine($"{reference.TryGetTarget(out var target)}: {target}");
DoSomething(x => DoAnotherThing(x));
}
需要验证的是 MainPage
对象是否被回收。然而在这段代码中,MainPage
并没有被回收;然而去掉最后一行,MainPage
便可以正常回收。关键是,即便是在 Console.WriteLine 上打下断点,让代码永远不会执行到最后一句,也不会改变回收的结果。
由于 DoSomething
中的委托参数恰好就是 MainPage
类型的,不禁让人觉得可能是此函数做了一些奇怪的事情。然而毕竟参数中传入的委托参数只是形参,理论上不应该影响到外部对象的回收。那么影响的只可能是变量的捕获了。
于是,我们将最后一行换成别的函数别的参数:
DoSomething(null);
或者将整个这一句提取成新的函数:
private void OnLoaded(object sender, RoutedEventArgs e)
{
// 省略前面的代码。
ExtractedMethod();
}
private void ExtractedMethod()
{
DoSomething(x => DoAnotherThing(x));
}
那么,回收就会正常进行。
现在,不执行这个受争议的函数了,我们使用空的匿名函数。
private void OnLoaded(object sender, RoutedEventArgs e)
{
var variable = new MainPage();
var reference = new WeakReference<MainPage>(variable);
variable = null;
GC.Collect();
Console.WriteLine($"{reference.TryGetTarget(out var target)}: {target}");
Dispatcher.InvokeAsync(() => { });
}
一样会导致不回收。
结论
在微软官方的《C# 规范 5.0》(点此下载)的第 7.15.5.1 章节中有说到:
When an outer variable is referenced by an anonymous function, the outer variable is said to have been captured by the anonymous function. Ordinarily, the lifetime of a local variable is limited to execution of the block or statement with which it is associated (§5.1.7). However, the lifetime of a captured outer variable is extended at least until the delegate or expression tree created from the anonymous function becomes eligible for garbage collection.
匿名函数会捕获当前上下文的局部变量,延长对象的生命周期;直到此委托或表达式树被回收掉。
也就是说,只要某个方法中存在没有被回收的匿名函数/lamda 表达式/表达式树,那么当前上下文的对象直到这些匿名函数被回收之前都不会被回收,即便已经设为了 null。
参考资料
- c# - .NET Do lambdas prevent garbage collection of external references used in them? - Stack Overflow
- C# Language Specification 5.0
- C# 6.0 draft Language Specification - Microsoft Docs
C#/.NET 匿名函数会捕获变量,并延长对象的生命周期的更多相关文章
- JS里面匿名函数的调用 & 变量作用域的实验
参考 http://www.educity.cn/wenda/54753.html 已实验验证结果正确. 1.下列哪些正确?(B.C) A.function(){ alert("Here!& ...
- C的变量类型、作用域与生命周期的总结
C的变量类型.作用域与生命周期的总结 最近在看"C Programing Language" (Kernighan, Ritchie)关于外部变量的讨论,之前在学C的时候对这些ex ...
- C++ 匿名对象的生命周期
//匿名对象的生命周期 #define _CRT_SECURE_NO_WARNINGS #include<iostream> using namespace std; class Poin ...
- JavaScript 中的匿名函数((function() {})();)与变量的作用域
以前都是直接用前端框架Bootstrap,突然想看看Javascript,发现javascript是个非常有趣的东西,这里把刚碰到的一个小问题的理解做下笔录(废话不多说,上代码). /** * Exa ...
- C#2匿名方法中的捕获变量
乍一接触"匿名方法中的捕获变量"这一术语可能会优点蒙,那什么是"匿名方法中的捕获变量"呢?在章节未开始之前,我们先定义一个委托:public delegate ...
- js循环函数中的匿名函数和闭包问题(匿名函数要用循环中变量的问题)
js循环函数中的匿名函数和闭包问题(匿名函数要用循环中变量的问题) 一.总结 需要好好看下面代码 本质是因为匿名函数用到了循环中的变量,而普通方式访问的话,匿名函数的访问在循环之后,所以得到的i是循环 ...
- js匿名函数(变量加括号就是函数)
js匿名函数(变量加括号就是函数) 一.总结 变量加括号就是函数,而函数的括号是用来传参的 1.类比:以正常函数去想匿名函数,匿名函数比正常函数只是少了函数名,本质还是一样,该怎么传参还是怎么传参,小 ...
- 浅析匿名函数、lambda表达式、闭包(closure)区别与作用
浅析匿名函数.lambda表达式.闭包(closure)区别与作用 所有的主流编程语言都对函数式编程有支持,比如c++11.python和java中有lambda表达式.lua和JavaScript中 ...
- 前端JS面试题汇总 Part 2 (null与undefined/闭包/foreach与map/匿名函数/代码组织)
原文:https://github.com/yangshun/front-end-interview-handbook/blob/master/questions/javascript-questio ...
随机推荐
- git开发错分支
1,代码未提交时: 使用以下命令即可解决. git add . (把所有改动暂存) git stash (把暂存的文件提交到git的暂存栈) git checkout 本该提交代码的 ...
- codevs 1085 数字游戏 dp或者暴搜
1085 数字游戏 2003年NOIP全国联赛普及组 时间限制: 1 s 空间限制: 128000 KB 题目描述 Description 丁丁最近沉迷于一个数字游戏之中.这个游戏看似简单 ...
- 关于ckeditor 之 上传功能
度了很多文章,看了很多关于ckeditor配置上传功能的文章,没一个写得清楚的, 就是简单的根目录下.config.js 增加 config.filebrowserUploadUrl="/a ...
- 【Python】 简易实现接口测试自动化
实现思路 使用excel管理用例用例信息,requests模块发送http请求,实现了记录日志,邮件发送测试报告的功能 目录结构如下: 下面直接上代码: 统筹脚本 # -*- coding:utf-8 ...
- js的一些编码问题
1 eval()的使用; 未声明变量的使用: 遗漏的分号; 不恰当的换行; 错误的逗号使用; 语句周围遗漏的括号; switch分支语名中遗漏的break; 重复声明的变量; with的使用; 错误使 ...
- Kafka消息文件存储
在对消息进行存储和缓存时,Kafka依赖于文件系统.(Page Cache) 线性读取和写入是所有使用模式中最具可预计性的一种方式,因而操作系统采用预读(read-ahead)和后写(write-be ...
- java实现赋值excel模板,并在新文件中写入数据,并且下载
/** * 生成excel并下载 */ public void exportExcel(){ File newFile = createNewFile(); //File newFile = new ...
- 热备模式相关问题2.txt
--//上午测试热备模式相关问题,就是如果打开热备模式,如果中间的归档丢失,oracle在alter database end backup ;时并没有应用日志. --//虽然热备份模式文件头scn被 ...
- guava的事件发布订阅功能
事件的重要性,不用说很重要,在很多时候我们做完一个操作的时候,需要告知另外一个对象让他执行相应操作,比如当用户注册成功的时候,需要抛出一个注册成功的事件,那么有监听器捕获到这个事件,完成后续用户信息初 ...
- cat 命令|more命令|less命令
cat主要有三大功能:1.一次显示整个文件:cat [-n] filename2.从键盘创建一个文件:cat > filename 3.将几个文件合并为一个文件:cat file1 file2 ...