C#中闭包的陷阱

在最近的项目中,我封装一个异步执行程序的组件。这个组件的功能就是在执行一些耗时的后台操作(比如连数据库读取数据,或者后台进行一些统计计算)的时候不阻塞UI,再弹一个转圈圈的动画告诉用户正在执行后台操作。方法包含三个参数,其中前两个是委托类型,分别是耗时操作的doWork和操作完成后的回调callBack。第三个参数就是弹出的等待动画旁边的提示消息,类似于“正在查找数据,请稍后。。。"之类的文本,由调用方设定。方法代码大概长下面这个样子:

 public static void Run(Action doWorker, Action callBack, string message)
        {
           //todo:method body
        }

因为这个组件我设计成不依赖于任何其他窗体,也就是说这个组件是一个独立的window,而不是作为一个控件插入到其他窗体。在其他任何window里调用都是直接调用这个静态方法即可在调用的window前弹出一个等待窗口,并且调用的地方也没有阻塞。那么问题来了,在这个方法里我怎么才知道是哪个window或者Control在调用呢?我采用的方法是使用doWorker的Target属性来获取当前调用的window,把它赋给组件的Owner,再把组件的启动位置设为Owner的中央即可。代码如下:

this.Owner = doWorker.Target as Window;if(this.Owner == null){  this.Owner = Window.GetWindow(doWorker.Target as Control);
}
this.Show();

嗯,大功告成。run起来很顺畅,直到一段代码的出现。那是下面这样一段代码:

var orderTable = grd_Order.DataSource as DataTable;
if(orderTable == null) return;
MsgWindow.Run(()=>{
        DbHepler.Update(orderTable);
    },null,"正在保存数据,请稍后!);

MsgWindow正是我封装的组件的类名。不知道为什么,这段代码调用时钟出错,异常出在Run方法中的这句代码:this.Owner = Window.GetWindow(Traget as Control);

因为调用组件的也许可能不是Window而是一个UserControl,而这句代码正是针对调用方是UserControl的。报的异常是说Window.GetWindow的参数不能为null,也就是说doWorker.Target as Control失败了,doWorker.Target根本不是一个Window,也不是一个Control。这怎么可能?!!代码明明就是写在一个自定义的Control。而且这个Control中还有另外一处也是调用的Run方法,参数都差不多,那里就没问题,运行得很顺畅。为什么就这个调用失败了呢?

经过跟踪调试,发现原来问题出在调用的地方。调的地方传第一个参数doWorker的时候,使用了闭包。在这个匿名方法中引用了外部变量orderTable。我想起之前看过的关于C#闭包的实现原理,其实是将引用的外部对象包装在一个匿名的类里了,引用的外部对象作为这个匿名类的一个成员。在这里,编译的时候C#编译器默默的为orderTable变量生成了一个匿名的类,orderTable作为这个匿名类的成员而保存了下来。要不是这么处理的话,还没来得及执行匿名方法,orderTable就已经超出了作用域而不可访问了。

找到问题根源后,解决方案也就呼之欲出了。其实这里根本用不着闭包,将orderTable放在匿名方法里面即可解决。如下:

if(orderTable == null) return;
MsgWindow.Run(()=>{
        var orderTable = grd_Order.DataSource as DataTable;
        DbHepler.Update(orderTable);
    },null,"正在保存数据,请稍后!);    
时间: 2024-07-28 20:34:08

C#中闭包的陷阱的相关文章

JavaScript中的this陷阱的最全收集--没有之一

原文:JavaScript中的this陷阱的最全收集--没有之一 当有人问起你JavaScript有什么特点的时候,你可能立马就想到了单线程.事件驱动.面向对象等一堆词语,但是如果真的让你解释一下这些概念,可能真解释不清楚.有句话这么说:如果你不能向一个6岁小孩解释清楚一个东西,那么你自己也不懂这个东西.这句话或许有点夸张,但是极其有道理.个人觉得,如果需要掌握一门语言,掌握它的API只是学了皮毛,理解这门语言的精髓才是重点.提及JavaScript的精髓,this.闭包.作用域链.函数是当之无

编写高质量代码改善C#程序的157个建议[C#闭包的陷阱、委托、事件、事件模型]

前言 本文已更新至http://www.cnblogs.com/aehyok/p/3624579.html .本文主要学习记录以下内容: 建议38.小心闭包中的陷阱 建议39.了解委托的实质 建议40.使用event关键字对委托施加保护 建议41.实现标准的事件模型 建议38.小心闭包中的陷阱 首先我们先来看一段代码: class Program { static void Main(string[] args) { List<Action> list = new List<Action

JavaScript中的this陷阱的最全收集

当有人问起你JavaScript有什么特点的时候,你可能立马就想到了单线程.事件驱动.面向对象等一堆词语,但是如果真的让你解释一下这些概念,可能真解释不清楚.有句话这么说:如果你不能向一个6岁小孩解释清楚一个东西,那么你自己也不懂这个东西.这句话或许有点夸张,但是极其有道理.个人觉得,如果需要掌握一门语言,掌握它的API只是学了皮毛,理解这门语言的精髓才是重点.提及JavaScript的精髓,this.闭包.作用域链.函数是当之无愧的.这门语言正式因为这几个东西而变得魅力无穷. 博客的标题是<J

转:JavaScript中的this陷阱的最全收集

在其他地方看到的,觉得解释的狠详细,特此分享 当有人问起你JavaScript有什么特点的时候,你可能立马就想到了单线程.事件驱动.面向对象等一堆词语,但是如果真的让你解释一下这些概念,可能真解释不清楚.有句话这么说:如果你不能向一个6岁小孩解释清楚一个东西,那么你自己也不懂这个东西.这句话或许有点夸张,但是极其有道理.个人觉得,如果需要掌握一门语言,掌握它的API只是学了皮毛,理解这门语言的精髓才是重点.提及JavaScript的精髓,this.闭包.作用域链.函数是当之无愧的.这门语言正式因

JavaScript闭包中闭包函数this的指向

阅读文章前, 请先阅读阮一峰老师的这篇文章http://www.ruanyifeng.com/blog/2009/08/learning_javascript_closures.html 前人写过的, 而且写得很好, 就没必要重复下去了. 只加一些阮老师的文章里没有说的.顺便总结一下. 引用一句话: "闭包就是functions that return function"(出处已经忘记啦) 闭包的类型: 循环闭包, 函数闭包 闭包的特点: 外部访问函数内部的值, 函数内部变量不被回收

关于js中闭包的理解

1.以前很不理解js中闭包的概念及使用,下面来看一下 function foo() { var a = 123; var b = 456; return function () { return a; } } var fn = foo(); 上面的代码只能访问 a和b,但是不能修改,这是js中闭包的技术之一 function foo() { var a = 123; var b = 456; return { get_a: function () { return a; }, set_a: fu

javascript中闭包的原理与用法小结(转)

一.在javaScript中闭包的五种表现形式如下: 1 /** 2 * Created by admin on 2016/12/26. 3 *//* 4 //向函数对象添加属性 5 function Circle(r){ 6 this.r=r; 7 } 8 Circle.prototype.PI=3.1415926; 9 Circle.prototype.area=function(){ 10 return this.PI*this.r*this.r; 11 }; 12 var c=new C

第二话:javascript中闭包的理解

闭包是什么? 通过闭包,子函数得以访问父函数的上下文环境,即使父函数已经结束执行. OK,我来简单叙述下,先上图. 都知道函数是javascript整个世界,对象是函数,方法是函数,并且js中实质性的面向对象相关也都是函数来实现和延伸,例如:"类". window:是指js中window类,也是js最高一层,因为什么这么说,因为你所有创建的方法和属性其实都在window之内.window中的所有方法,在自己创建的方法中都可以调到.可以仔细想想alert,在任何地方都可以alert,其实

Lua中闭包详解 来自RingOfTheC[[email&#160;protected]]

这些东西是平时遇到的, 觉得有一定的价值, 所以记录下来, 以后遇到类似的问题可以查阅, 同时分享出来也能方便需要的人, 转载请注明来自RingOfTheC[[email protected]] 这里, 简单的记录一下lua中闭包的知识和C闭包调用 前提知识: 在lua api小记2中已经分析了lua中值的结构, 是一个 TValue{value, tt}组合, 如果有疑问, 可以去看一下 一些重要的数据结构 lua中有两种闭包, c闭包和lua闭包 两种闭包的公共部分: #define Clo