本文转载:http://blog.csdn.net/a0700746/article/details/4473796
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
|
一般在百度搜一下,会出来一下内容,看来很好用。Singleton很方便的一个用处就是控制窗体被多次或重复打开。下面是它的用法。 一般Singleton模式通常有几种形式: public
class Singleton {
private
Singleton(){}
//在自己内部定义自己一个实例,是不是很奇怪?
//注意这是private 只供内部调用
private
static Singleton instance = new
Singleton();
//这里提供了一个供外部访问本class的静态方法,可以直接访问
public
static Singleton getInstance() {
return
instance;
} } 第二种形式: public
class Singleton {
private
static Singleton instance = null ;
public
static synchronized Singleton getInstance() {
if
(instance== null )
instance= new
Singleton();
return
instance; }
} 使用Singleton.getInstance()可以访问单态类。 上面第二中形式是lazy initialization,也就是说第一次调用时初始Singleton,以后就不用再生成了。 注意到lazy initialization形式中的synchronized,这个synchronized很重要,如果没有synchronized,那么使用getInstance()是有可能得到多个Singleton实例。关于lazy initialization的Singleton有很多涉及 double - checked
locking (DCL)的讨论,有兴趣者进一步研究。
一般认为第一种形式要更加安全些。 使用Singleton注意事项: 有时在某些情况下,使用Singleton并不能达到Singleton的目的,如有多个Singleton对象同时被不同的类装入器装载;在EJB这样的分布式系统中使用也要注意这种情况,因为EJB是跨服务器,跨JVM的。 我们以SUN公司的宠物店源码(Pet Store 1.3.1)的ServiceLocator为例稍微分析一下: 在Pet Store中ServiceLocator有两种,一个是EJB目录下;一个是WEB目录下,我们检查这两个ServiceLocator会发现内容差不多,都是提供EJB的查询定位服务,可是为什么要分开呢?仔细研究对这两种ServiceLocator才发现区别:在WEB中的ServiceLocator的采取Singleton模式,ServiceLocator属于资源定位,理所当然应该使用Singleton模式。但是在EJB中,Singleton模式已经失去作用,所以ServiceLocator才分成两种,一种面向WEB服务的,一种是面向EJB服务的。 Singleton模式看起来简单,使用方法也很方便,但是真正用好,是非常不容易,需要对Java的类 线程 内存等概念有相当的了解。 总之:如果你的应用基于容器,那么Singleton模式少用或者不用,可以使用相关替代技术。 以下是本人在实际的项目中的具体灵活运用,希望能帮到大家的忙。 问题描述:用了单件模式Singleton来控制窗体被重复或多次打开,最初是在MDI子窗体中写方法,后来看别人的写的是属性,基本是一样的,都可以,然后想的是,项目中有诺多窗体,如果每个窗体都写一份,岂不太脑残?所以打算写一个基类,但是呢,Singleton只能被实例化一次,所以只好用了泛型来写。基类终于写好了,然后调试,发现确实可以控制窗体的打开个数,即被多次打开,但是呢,当窗体操作完成并关闭后,,再次打开这个窗体时就会出现(无法访问已释放的对象)的错误。 这里呢就有关系到C#中的垃圾回收问题。C#垃圾回收器管理所有的托管对象,所有需要托管数据的.NET语言(包括 C#)都受运行库的垃圾回收器的制约。垃圾回收器可以确定运行垃圾回收的最佳时间,自动进行垃圾回收。然而垃圾回收的一个产物是:C#对象没有确定性毁坏。所以会出现子窗口对象已被销毁,但又不为 null ,故出现访问时产生“未处理 ObjectDisposedException”异常(来自于“从小处看C#.net垃圾回收”一文)。
这里呢关于这个问题,因为不是主体所涉及,就不过多赘述,有兴趣的请大家自己查阅相关资料。回到正题,这又怎么办呢,有办法。问题的关键是,窗体已被释放,但因为还未来得及被系统处理,所以有Instance.Isdisposed= true ,而Instance却不等 null ;怎么办呢,在窗体关闭的时候,可以人为地将其置为 null ;然后问题又出来了,Singleton为只读啊,如果你为Instance赋值,系统会报错,Instance为只读。怎么办呢,没办法,不撞南墙不回头,下定决心就做下去。要让它能被赋值,只能加上一个 set 了,这样再调试,一切就问题解决了。首先,窗体的重复或多次打开问题解决了;然后呢,每个窗体打开时,只要一行代码就搞定了,不用每个窗体写一份属性,方法(其实质还是借鉴Singleton);最后呢,不会出现再次打开不能访问的问题,即无法访问已释放的对象的问题。
以下是Singleton基类C#代码: using
System;
using
System.Collections.Generic;
using
System.Linq;
using
System.Text;
namespace
dchtgl
{ //泛型实现实例单件化
public
class Singleton<T> where
T : new ()
{
private
static T instance = default (T);
private
static readonly object lockHelper = new
object ();
private
Singleton() { }
public
static T Instance
{
get
{
if
(instance == null )
{
lock
(lockHelper)
{
if
(instance == null )
{
instance = new
T();
}
}
}
return
instance;
}
set
{
instance = value;
}
}
}
} 如果要在主窗体中打开某个窗体,比如Form1,代码如下: //Form1为MDI子窗体 private
void 会员管理ToolStripMenuItem_Click( object
sender, EventArgs e)
{
Form1 F1 = Singleton<Form1>.Instance;
F1.MdiParent = this ;
F1.Show();
}
但要注意的是,必须在该窗体关闭时,加上 private
void Form1_FormClosed( object
sender, FormClosedEventArgs e)
{
Singleton<Form1>.Instance = null ;
}
这样才不会出问题。这回应该清楚了吧。 对于这个问题,最近有了新的认识,把代码贴出来以飨读者。 using
System;
using
System.Collections.Generic;
using
System.Linq;
using
System.Text;
using
System.Windows.Forms;
namespace
dchtgl
{ /// <summary>
/// 泛型实现窗体实例单件化
/// </summary>
/// <typeparam name="T">窗体类</typeparam>
public
static class Singleton<T> where
T : Form, new ()
{
private
static T instance = default (T);
private
static readonly object lockHelper = new
object ();
/// <summary>
/// 获取窗体的唯一实例
/// </summary>
public
static T Instance
{
get
{
if
(instance == null )
{
lock
(lockHelper)
{
if
(instance == null )
{
instance = new
T();
//加上实例关闭事件,窗体就会自动回收,即instance=null;
instance.FormClosed += new
FormClosedEventHandler(DestroyForm);
}
}
}
return
instance;
}
}
/// <summary>
/// 当窗体关闭时将Instance置空
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private
static void DestroyForm( object
sender, FormClosedEventArgs e)
{
instance = default (T);
}
}
} 用法同上,但是不必在窗体关闭的时候,加上 private
void Form1_FormClosed( object
sender, FormClosedEventArgs e)
{
Singleton<Form1>.Instance = null ;
}
这一句了。 |