博客园  :: 首页  :: 新随笔  :: 订阅 订阅  :: 管理

转载:.net 中慎用static

Posted on 2011-04-02 09:50  sigo-yr  阅读(400)  评论(0编辑  收藏  举报
在.Net平台下进行CS软件开发时,我们经常遇到以后还要用到某些变量上次修改后的值,为了简单起见,很多人都习惯用static来定义这些变量,我也是。这样非常方便,下一次调用某个函数时该变量仍然保存的是处理过的值,直接拿来用就可以了。
        现在转入了BS软件开发,我们很自然地会沿用这种习惯。如在页面中统计某个按钮被按下的次数,先在类中OnClick事件的处理过程前定义一static变量times,则每次调用该按钮的OnClick事件时,令times增1即可,非常方便:

[C#]:
...
static int times=0;
...
private void Button1_Click(object sender,EventArgs e)
{
   times++;
   Label1.Text=times.ToString();
}
        在我们庆幸如此方便之余,就未曾意识到我们已经埋下了一棵难以察觉的定时炸弹。为什么哪?
        这还要从Asp.net的运行机制谈起。在CS模式软件开发过程中,我们通常不会关心应用程序是在哪里运行的,变量存放在哪里,客户端程序就运行在客户 端,服务器端程序就运行在服务器端,一般情况下,二者除了数据库中的数据外基本没有其他共享的东西。所以这时客户端的用户大可放心的使用static变 量,因为它们就存放在客户端程序中。
        于是我们就习惯的在做BS模式的页面时也用static变量,殊不知Asp.net中的static已不同于CS中的static。原因很简单,就是因为 在Asp.net中所有的用户将使用同一个static变量。这就意味着每一个使用该页面的用户对该变量的操作将会影响到其他用户。就拿上面计数器的例子 来说,假设times初试值为0,因为此时只有我们自己在使用这个页面,当然不会有什么问题,但如果有两个人同时连接到这个页面,如果A单击了 Button1一次,则B刷新页面后Label1将显示1,如果B再单击Button1一次,则times变成2,两个人刷新页面后就出现问题了:A和B 都会说,我明明只单击了Button1一次,怎么Label1就显示我单击了两次哪?——这就是因为两个人共用的是服务器上同一个times,任何一个人 对times的操作都会在使用该页面的他人的浏览器中表现出来。问题就出在这里。
        怎么办哪?还好,除了传统的Asp中的Session对象外,Asp.net提供了一个更好的ViewState对象。ViewState对象用来保存页 面中的各种变量,甚至是对象。使用方法和HashTable类似,只要用变量名称做索引,如ViewState["Var"],就可以用存取变量Var的 值,而不管Var是普通变量,还是对象,甚至是内存中的一张DataTable,太方便了。为什么可以用ViewState而不能用static变量哪? 原因就是服务器端会为每个连接到该页面的用户分别建立一个ViewState,所以ViewState相当于页面级的Session。这下我们可以放心地 使用ViewState来存取需要暂寸的变量和对象了。
        ViewState的用法很简单,如下所示:
        1、保存变量到ViewState中:
       ViewState["times"]=times;//存放普通变量times
       ViewState["Orders"]=dtOrders;//存放DataTable型对象dtOrders
        2、读出ViewState中的值:
       times=(int)ViewState["times"];
       dtOrders=(DataTable)ViewState["Orders"];
        看见了吧?就如此简单!有的朋友会问读出变量的值时为什么要进行强制类型转换?这是因为当变量(不管是int型的普通变量times,还是 DataTable型的对象dtOrders)被存放到ViewState中后,ViewState可不管你是普通变量还是对象,统统按Object来对 待。所以当我们取出存放在ViewState中的东西时,一定要转换成相应的类型,否则就会报错。而这一操作不用在用ViewState保存变量时进行, 系统会自动转换。(注意ViewState括号中的字符串只是为了标识不同变量的索引,用不着非要和变量同名)所以上面计数器的代码应该这样写才好:
       ...
       ViewState["times"]=0;
       ...
       private void Button1_Click(object sender,EventArgs e)
       {
           int times=(int)ViewState["times"];
           times++;
           ViewState["times"]=times;
           Label1.Text=times.ToString();
        }
        这是不是说static型变量就没用了哪?当然不是!在C#中用static声明的类不用实例化直接使用。正是由于所有用户共享服务器端的同一个 static变量,所以可以用static型对象来存取一些公用的处理模块,比如类型转换、变量验证等工作。所以要根据具体情况而定。
        还有一点需要注意:如果在页面中多个过程要共享一个对象或变量,我们在页面类的开始部分定义一个页面级的全局变量是不行的,static本来可以,但上面说了这种类型的变量不安全,所以这时就可以用ViewState。
        好了,这下我们可以放心的暂存某些变量或对象了。
        但在随后的实践中我发现,在页面中还是尽量少用ViewState变量,因为ViewState变量在客户端实际上是用<input type="hidden" value="ADFAIB3P234P-AFAFAF......"/>保存的一个对象,这样如果要保存的是个对象,甚至是个很复杂的对象(如 DataTable),这样以来就会增加网络传输的负担。所以在使用时一定要注意。

//////////////////////////////////////////////////////

“由于Aplication,static member是全局变量,而我们是在多线程服务器环境写程序,对他们的使用需要注意线程安全的问题”,这一点我完全同意,对于线程同步的问题,再补充一下:

如果在页面中有对 static 变量的写入操作,应该在代码中进行 Lock ,或者使用原子操作 Interlocked 类。

假如有Application["A"]、Application["B"]、Application["C"],有线程访问Application["A"]其他线程不能访问Application["B"] and Application["C"]。
我对这一点表示怀疑,读取操作应该不会导致 Lock,只有在进行写入的时候才会 Lock。
这 一点在 HttpApplicationState 类(Application 对象是 HttpApplicationState 类的一个实例)内部是使用一个 ReadWriteObjectLock 对象来操作的,在读取进行时计数加 1,读取完成时计数减 1,写入是排他的,要等到计数为零才能开始写入,写入过程中会将计数置为 -1,防止其他线程进入读取。

如果使用 Application 对象,仅在 Application 集合中的两个或多个对象之间关系密切的时候,才需要使用 Lock(Application) 或者 Application.Lock() ,其他情况下几乎不需要考虑线程同步的问题。

这样看来,对于一般的操作,Application 对象就足够了。static 成员则更有利于类的逻辑封装和重用。

举个 static 成员使用的例子:一个 WebForm 程序和一个 WinForm 程序,要用到同一份数据,同样需要用到缓存技术,可以使用 static 成员将逻辑封装为一个 class,这个 class 就可以同时用在 WebForm 和 WinForm 当中。

转自:http://hi.baidu.com/crp8/blog/item/075e951886c68fb34bedbc68.html

     http://www.cnblogs.com/sadier/articles/23225.aspx

流量统计