温故知新---重读C#InDepth(二)
一本好书,或是一本比较有深度的书,就是每次研读的时候都会有新的发现。
好吧,我承认每次读的时候都有泛泛而过的嫌疑~~
这几年一直专注于C#客户端的开发,逐步从迷迷糊糊,到一知半解,再到自以为是,最后沉下心重新审视。也许这也是一种进步一种自我学习的过程。
前面啰嗦了这么多,希望大家也能不那么浮躁的“深入理解”C#这门语言的每个知识点。本文总结书本中的知识,在结合实际应用场合进行概述,如果有不正确的地方,还请不吝指教。
文章中的内容比较浅显,请高手略过此文。
4. 程序闭包
程序闭包的问题是由于程序对某些变量进行了预判和处理(个人理解,若有误或不足请指正)使得某些变量理应作为值类型却变为了引用类型导致数据异常。
当然,大多数情况下,我们是不会遇到这样的问题,但在某些情况下,我们不得不注意并分析问题的根本原因,BUG永远不是随机的。
下面通过几个例子逐步来理解闭包的概念:
例4.1
private void Button1_Click(object sender, RoutedEventArgs e)
{
int outerVariableCaptured = ; // 外部变量(被捕获)
int outerVariableUnCaptured = ; // 外部变量(未捕获) if (DateTime.Now.Hour <= )
{
int normalLocalVariable = ; // 普通方法的局部变量,不是外部变量,因为在其作用域内无匿名方法。
this.Txb_Msg.Text += string.Format("普通方法的局部变量 = {0}", normalLocalVariable) + System.Environment.NewLine;
} Action x = new Action(() =>
{
int anonLocal = ; // 匿名方法的局部变量
this.Txb_Msg.Text += string.Format("匿名方法的局部变量 = {0}", anonLocal) + System.Environment.NewLine;
this.Txb_Msg.Text += string.Format("匿名方法中被捕获的外部变量 = {0}", outerVariableCaptured)
+ System.Environment.NewLine; // 匿名方法中调用了作用域外的变量,所以变量变为被捕获的外部变量
}); this.Txb_Msg.Text += string.Format("普通方法的未捕获的外部变量 = {0}", outerVariableUnCaptured) + System.Environment.NewLine; x();
}
输出结果:
普通方法的局部变量 =
普通方法的未捕获的外部变量 =
匿名方法的局部变量 =
匿名方法中被捕获的外部变量 =
4.1中是让大家了解外部变量,局部变量,捕获等相关概念。其中最重要的是被捕获的外部变量。
例4.2
private void Button3_Click(object sender, RoutedEventArgs e)
{
// 证明被捕捉的局部变量声明周期被延长了。
OnCreateDelegate += MainWindow_OnCreateDelegate; this.Dispatcher.Invoke(OnCreateDelegate(this)); // 此处Invoke容易引起歧义:原因在于Invoke事件之后返回的还是一个事件。
// Counter是值类型,逃脱其作用域时栈上数据会被回收,真实的情况是这样吗?
// 从另一个侧面也说明了,值类型是在栈上还是堆上,依赖于创建对象的类型。
}
private Delegate MainWindow_OnCreateDelegate(object sender)
{
var frm = sender as MainWindow;
int Counter = ;
var a = new Action(() =>
{
frm.Txb_Msg.Text += string.Format("委托内部的变量值 = {0}", Counter) + System.Environment.NewLine;
Counter++;
});
a(); return a;
}
输出:
委托内部的变量值 =
委托内部的变量值 =
这个例子要说明的是Counter其值类型原本的生存周期应该在MainWindow_OnCreateDelegate(object sender)方法中,可是偏偏却逃离了方法的作用域,这就是我们所说的值类型是在堆上Or栈上
完全取决于其初始化的位置是在栈上还是在堆上。
例4.3
private void Button4_Click(object sender, RoutedEventArgs e)
{
// 更复杂的一些情况
var methods = new Action[];
int outside = ; // 实例化变量一次
for (int i = ; i < ; i++)
{
int inside = ; // 实例化变量多次
methods[i] = new Action(() =>
{
this.Txb_Msg.Text += string.Format("Inside Value = {0}; Outside Value = {1} ", inside, outside) + System.Environment.NewLine;
inside++;
outside++; // 匿名方法捕获的变量
});
} methods[].Invoke();
methods[].Invoke();
methods[].Invoke();
methods[].Invoke(); }
输出结果:
/***************Outside变量内存共享*************
* Inside Value = 100; Outside Value = 10
* Inside Value = 101; Outside Value = 11
* Inside Value = 102; Outside Value = 12
* Inside Value = 100; Outside Value = 13
* *******************************************/
这个例子就得好好想想了,outside和inside的值到底会是什么?为什么会这样?
原因在于inside在For循环的内部初始化了多次,也就是说For循环几次,就有几个独立的inside对象,虽说它是值类型。
例4.4
这个例子摘自《编写高质量代码:改善C#程序的157个建议》
private void Button5_Click(object sender, RoutedEventArgs e)
{
// 闭包陷阱
var methods = new Action[];
for (int i = ; i < ; i++)
{
int inside = i; // 实例化变量多次
methods[i] = new Action(() =>
{
this.Txb_Msg.Text += string.Format("Inside Value = {0}; Index Value = {1} ", inside, i) + System.Environment.NewLine;
});
}
methods[].Invoke();
methods[].Invoke(); /*****************闭包陷阱*********************
* 当使用i的值时,i就是前面说的共享变量(捕获的外部变量),所以总是输出i的最大值。
* 当使用Inside值时,Inside就是内部变量,每次创建对象都重新生成,所以此处inside的值是递增,即缓存下i的值。
* 对于IL,其创建了Tempclass.i来代替i,导致了i值共享。
* *******************************************/
}
输出结果:
Inside Value = ; Index Value =
Inside Value = ; Index Value =
其实如果用ILDasm来看的话,针对i这个对象,IL生成了一个DisplayClass(就是一个名字而已)这样一个类,最总导致了,i变为引用类型,数据异常。
持续更新:示例代码下载
温故知新---重读C#InDepth(二)的更多相关文章
- 温故知新---重读C#InDepth(一)
一本好书,或是一本比较有深度的书,就是每次研读的时候都会有新的发现. 好吧,我承认每次读的时候都有泛泛而过的嫌疑~~ 这几年一直专注于C#客户端的开发,逐步从迷迷糊糊,到一知半解,再到自以为是,最后沉 ...
- ASP.Net WebForm温故知新学习笔记:二、ViewState与UpdatePanel探秘
开篇:经历了上一篇<aspx与服务器控件探秘>后,我们了解了aspx和服务器控件背后的故事.这篇我们开始走进WebForm状态保持的一大法宝—ViewState,对其刨根究底一下.然后,再 ...
- 温故知新——Spring AOP(二)
上一篇我们介绍Spring AOP的注解的配置,也叫做Java Config.今天我们看看比较传统的xml的方式如何配置AOP.整体的场景我们还是用原来的,"我穿上跑鞋",&quo ...
- Android——开源框架Universal-Image-Loader + Fragment使用+轮播广告
原文地址: Android 开源框架Universal-Image-Loader完全解析(一)--- 基本介绍及使用 Android 开源框架Universal-Image-Loader完全解析(二) ...
- 【温故知新】——BABYLON.js学习之路·前辈经验(二)
前言:在上一篇随笔BABYLON.js学习之路·前辈经验(一)中回顾了组内同事们长时间在Babylon开发实践中的总结出的学习之路和经验,这一篇主要对开发中常见的一些功能点做一个梳理,这里只作为温故知 ...
- 再回首,Java温故知新(二):Java基本数据类型
Java作为一种强类型语言,意味着每一个变量都会有特定的类型,Java共有8种基本类型,其中有4种整型(byte.short.int.long).两种浮点型(float.double).1种字符型(c ...
- 重读《深入理解Java虚拟机》二、Java如何分配和回收内存?Java垃圾收集器如何工作?
线程私有的内存区域随用户线程的结束而回收,内存分配编译期已确定,内存分配和回收具有确定性.共享线程随虚拟机的启动.结束而建立和销毁,在运行期进行动态分配.垃圾收集器主要对共享内存区域(堆和方法区)进行 ...
- 【温故知新】Java web 开发(二)Servlet 和 简单JSP
系列一介绍了新建一个 web 项目的基本步骤,系列二就准备介绍下基本的 jsp 和 servlet 使用. (关于jsp的编译指令.动作指令.内置对象不在本文讨论范围之内) 1. 首先,在 pom. ...
- 深入js的面向对象学习篇(封装是一门技术和艺术)——温故知新(二)
下面全面介绍封装和信息隐藏. 通过将一个方法或属性声明为私用的,可以让对象的实现细节对其它对象保密以降低对象之间的耦合程度,可以保持数据的完整性并对其修改方式加以约束.在代码有许多人参与设计的情况下, ...
随机推荐
- js清除缓存方法
1.加入如下头描述 <META HTTP-EQUIV="Pragma" CONTENT="no-cache"> <META HTTP-EQUI ...
- 最小的k个数
// 最小的k个数.cpp : 定义控制台应用程序的入口点. // #include "stdafx.h" #include <iostream> #include & ...
- hdu-5904 LCIS(水题)
题目链接: LCIS Time Limit: 4000/2000 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Others)Total ...
- HDU 3333 Turing Tree --树状数组+离线处理
题意:统计一段序列[L,R]的和,重复元素只算一次. 解法:容易看出在线做很难处理重复的情况,干脆全部讲查询读进来,然后将查询根据右端点排个序,然后离散化数据以后就可以操作了. 每次读入一个数,如果这 ...
- 并发用户数与TPS之间的关系
1. 背景 在做性能测试的时候,很多人都用并发用户数来衡量系统的性能,觉得系统能支撑的并发用户数越多,系统的性能就越好:对TPS不是非常理解,也根本不知道它们之间的关系,因此非常有必要进行解释. 2 ...
- 项目管理工具Redmine各功能测试
Redmine 是一个开源的.基于Web的项目管理和缺陷跟踪工具.它用日历和甘特图辅助项目及进度可视化显示.同时它又支持多项目管理.Redmine是一个自由开放 源码软件解决方案,它提供集成的项目 ...
- 使用CSS3画出一个叮当猫
刚学习了这个案例,然后觉得比较好玩,就练习了一下.然后发现其实也不难,如果你经常使用PS或者Flash的话,应该就会知道画个叮当猫是很容易的事,至少我是这么觉得.但是,用CSS3画出来确实是第一次接触 ...
- RAS RC4 AES 加密 MD5
这两者唯一的相同点是设计者中都包含了MIT的Ron Revist教授.RSA是公钥密码算法,优点:不用事先通过秘密信道传递密钥,可以用于数字签名.缺点:速度慢RC4是序列密码算法,优点:速度快,缺点: ...
- Windows下MemCache多端口安装配置
Windows下MemCache环境安装配置的文章很多,但大部分都是用的默认端口11211,如何修改默认端口.如何在一台服务器上配置多个MemCache端口?这正式本文要解决的问题. 1.从微软官网下 ...
- Caffe学习系列(6):Blob,Layer and Net以及对应配置文件的编写
深度网络(net)是一个组合模型,它由许多相互连接的层(layers)组合而成.Caffe就是组建深度网络的这样一种工具,它按照一定的策略,一层一层的搭建出自己的模型.它将所有的信息数据定义为blob ...