• 全部
  • C#综合技术
  • C#互联网桌面应用
  • AppLauncher
  • WinForm&WPF
  • C#开发新技术
  • 问答

C# 中单例模式的另一种实现

wt_sanlian 2011-04-19 06:41:24
最近C#版块中关于模式设计讨论比较多,偶也来参与参与,呵呵,与大家交流一下。

本文就单例模式进行一些讨论,在实际应用中,单例模式是经常需要的一种模式,

如在应用启动时,可以从数据库中读取一些配置,放在单例中,软件运行时,从单

例中获取参数等,以及一些设备通讯控制的应用等。

现假设有这样一个简单应用:
 
   从控制台读取一个字符串,然后将字符串打印在控制台上。

定义两个类:
   
public class TReader // 负责从控制台上读取一串字符
{
public string Read()
{
Console.WriteLine("Please input a str:");
return Console.ReadLine();
}
}

public class TPrinter // 将字符串打印在控制台上。
{
public void Print(string str)
{
Console.WriteLine("inputed:" + str);
}
}



很简单,是吧, 但如果这里要求将TReader和TPrinter以单例模式运行,于是TReader的代码就变成

了这个样子,TPrinter也类似.

public class TReader
{
public static TReader Instance
{
get
{
if (_Instance == null)
{
_Instance = new TReader();
}
return _Instance;
}
}

private static TReader _Instance;
public string Read()
{
Console.WriteLine("Please input a str:");
return Console.ReadLine();
}
}

public class TPrinter
{
public static TPrinter Instance
{
get
{
if (_Instance == null)
{
_Instance = new TPrinter();
}
return _Instance;

}
}

private static TPrinter _Instance;

public void Print(string str)
{
Console.WriteLine("inputed:" + str);
}
}


在每个类里各增加一个静态属性,我想说的问题就在这里,


1.单例模式是全局的行为,是类的外部应用,将这种静态属性设计在类里,显得很古怪。
2.两个类里的Instance实现代码几乎完全一模一样,代码重复,很不雅观!

那么怎么改善这种结构呢,答案就是利用C#的 泛型,

定义这样的一个泛型类,将Instance从TReader和TPrinter中提取出来 (含完整代码)。

public class G_<T> where T : class,new()
{
public static T Instance
{
get
{
if (_Instance == null)
{
_Instance = new T();
}
return _Instance;
}
set
{
_Instance = value;
}
}
private static T _Instance;
}

public class TReader // 负责从控制台上读取一串字符
{
public string Read()
{
Console.WriteLine("Please input a str:");
return Console.ReadLine();
}
}

public class TPrinter // 将字符串打印在控制台上。
{
public void Print(string str)
{
Console.WriteLine("inputed:" + str);
}
}

class Program // 主程序
{
static void Main(string[] args)
{
string str = G_<TReader>.Instance.Read(); //从控制台上读取字符串
G_<TPrinter>.Instance.Print(str); // 将字符串打印在控制台上
Console.ReadLine();
}
}


通过定义这样的一个泛型类,可实现所有类的单例模式,并且设计类时不再需要考虑
是不是要用到单例,提高了代码的可读性。
这样做实际上也有些缺点,如不能阻止在外部创建多个TPrinter或TReader的实例,一定
程度上违背了单例模式的原则。
...全文
510 点赞 收藏 21
写回复
21 条回复
切换为时间正序
当前发帖距今超过3年,不再开放新的回复
发表回复
wt_sanlian 2011-04-21
结了!
回复
wt_sanlian 2011-04-21
[Quote=引用 20 楼 smartls 的回复:]
我提一个建议:将你的G_<T>的构造函数中传入一个匿名委托Fun<T>参数,由外部的匿名委托来返回一个对象的实例,这样你就不必关心构造函数是否带参数的情况。

C# code

public class SingletonGenerator<T>
{
private object locker;
private bool initial……
[/Quote]

good!
回复
smartls 2011-04-21
我提一个建议:将你的G_<T>的构造函数中传入一个匿名委托Fun<T>参数,由外部的匿名委托来返回一个对象的实例,这样你就不必关心构造函数是否带参数的情况。

public class SingletonGenerator<T>
{
private object locker;
private bool initialized = false;
private Func<T> func;
private T instance;

public SingletonGenerator(Func<T> funcParam)
{
initialized = false;
func = funcParam;
locker = new object();
}

public T Value
{
get
{
if (!this.initialized)
{
lock (this.locker)
{
if (!this.initialized)
{
this.instance = this.func();
this.initialized = true;
}
}
}

return this.instance;
}
}
}
回复
smartls 2011-04-21
[Quote=引用 16 楼 wt_sanlian 的回复:]

1. 标准的单例模式中因为类的构造函数是私有的,所以也是不能带参数的。

2. 程序初始化时实例化所有的全局对象应该是一种标准的做法。"先建立程序的结构,然后再让程序工作",按开放闭合原则(只增加代码,不修改已有代码),若需要增加或修改现有功能时,需先增加一个类,然后只修改初始化过程中的一处代码。

3. 不需要修改现有的类,我要再增加一个泛型类

public class ……
[/Quote]

1.你这种泛型单例本来就不是标准的单例模式,是为了简化代码及编程推出的,既要利用泛型,又想控制私有函数,这是办不到的。况且"标准的"单例模式也确实允许构造一个带参数的构造函数。
2.你没明白我的意思。我指的是应用程序的"按需加载",在需要该资源的位置创建它的实例。例如你的程序为了完成发邮件功能,可能需要某全局变量A,但用户可能根本不想发邮件,你在初始化整个应用程序的时候初始化这处资源就浪费了性能。
3.增加新类的做法实际上也违反了程序设计的"dry原则"。
回复
wt_sanlian 2011-04-20

public class G2_<T> where T:class,new()
{
...
}




回复
wt_sanlian 2011-04-20
1. 标准的单例模式中因为类的构造函数是私有的,所以也是不能带参数的。

2. 程序初始化时实例化所有的全局对象应该是一种标准的做法。"先建立程序的结构,然后再让程序工作",按开放闭合原则(只增加代码,不修改已有代码),若需要增加或修改现有功能时,需先增加一个类,然后只修改初始化过程中的一处代码。

3. 不需要修改现有的类,我要再增加一个泛型类

public class G2_<ClassName> : where T:class,new()
{
...
}


回复
smartls 2011-04-20
[Quote=引用 3 楼 wt_sanlian 的回复:]

引用 1 楼 smartls 的回复:
如果泛型T包含含参数实例构造器,你怎么处理呢?


对于有参数的类,需要在外部实例化,即在程序初始化时进行

C# code

....
void AppInit()
{
G_<ClassName>.Instance = new ClassName( param1,param2);
}

....
[/Quote]

你这样做有3个问题:
1.泛型模板中的_instance = new T();代码要求类型必须具有默认无参数构造器,这样你不得不为每个具有参数构造器的类定义一个默认构造器。
2.含参构造器的初始化G_<ClassName>.Instance = new ClassName( param1,param2);与其他构造器的单例引用
方法不同,难以形成统一规范。况且程序初始化时实例化该类,一方面影响了程序加载性能,另一方面不一定做到"物有所需",即你的应用程序不一定会使用到该对象。甚至如果你的构造器参数包含其他对象,你不得不在之前初始化更多的对象。
3.另附一个稍有点吹毛求疵的理由,不要介意。单一的Instance对象引用只能指向类的同一个实例对象,但如果你的应用程序同时需要一个类的两种实例版本,受单例的约束,你是不是不得不在单例模板中定义一个新的Instance2属性呢?当然,这种情况不是很多见,算是吹毛求疵吧。
回复
cjh200102 2011-04-20
GOOD,确实不错
回复
不错,这样省了很多事
回复
wt_sanlian 2011-04-20
[Quote=引用 1 楼 smartls 的回复:]
如果泛型T包含含参数实例构造器,你怎么处理呢?
[/Quote]

对于有参数的类,需要在外部实例化,即在程序初始化时进行


....
void AppInit()
{
G_<ClassName>.Instance = new ClassName( param1,param2);
}

....
回复
wt_sanlian 2011-04-20
[Quote=引用 13 楼 dicksonworld 的回复:]
具有固定的“原则”才能称之为模式。
敢问楼主单例模式的原则是什么?
我看不出楼主的代码哪点与单例模式有关。。
[/Quote]

呵呵,有道理,那允不允许发明新的模式啊?
回复
dicksonworld 2011-04-20
具有固定的“原则”才能称之为模式。
敢问楼主单例模式的原则是什么?
我看不出楼主的代码哪点与单例模式有关。。
回复
jimh 2011-04-20
override FormClosed
{
base.Hide();
}
拦截Closed,不要关闭,隐藏起来就可以了。当然,Show的时候也要做点事,例如初始化一些对象,变量,值,等。
回复
m_Copper 2011-04-20
[Quote=引用 10 楼 wt_sanlian 的回复:]
楼上的问题最好是另开帖问,不过既然问了,俺就来按照俺的方法来解决下。
设: Fom1 是主窗口,上面有一个按钮,按下之后,显示Form2.
Form1中代码:

C# code

public partial class Form1 : Form
{
public Form1()
{
InitializeC……
[/Quote]

多谢。。。是一种解决办法
回复
wt_sanlian 2011-04-20
楼上的问题最好是另开帖问,不过既然问了,俺就来按照俺的方法来解决下。
设: Fom1 是主窗口,上面有一个按钮,按下之后,显示Form2.
Form1中代码:

public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}


private void button1_Click(object sender, EventArgs e)
{
G_Form_<Form2>.Instance.Show();

}
}


Form2.cs中代码

public partial class Form2 : Form
{
public Form2()
{
InitializeComponent();
}

}

public class G_Form_<T> where T : Form, new()
{
public static T Instance
{
get
{
if (_Instance == null)
{
_Instance = new T();
_Instance.FormClosed += new FormClosedEventHandler(_Instance_FormClosed);
}
return _Instance;
}
protected set
{
_Instance = value;
}
}

static void _Instance_FormClosed(object sender, FormClosedEventArgs e)
{
_Instance = null;
}
private static T _Instance;
}

实际上每次调用都创建了一个新的窗口,因为俺没找到关闭窗体时不释放资源的方法,
回复
m_Copper 2011-04-20
楼主高手,我想请教一个与单例模式有关的问题。
我新建一个winForm程序,出来默认产生的form1窗体,我有添加一个form窗体,我要把form2窗体做成单例窗体。代码如下:
public partial class FrmSingleton : Form
{
public FrmSingleton()
{
InitializeComponent();
}

private static FrmSingleton instance;

public static FrmSingleton Instance
{
get
{
if (instance == null)
instance = new FrmSingleton();
return FrmSingleton.instance;
}

}
}
然后我在form1中添加一个按钮,在click事件里添加如下代码:
if (e.Button == MouseButtons.Right)
FrmSingleton.Instance.Show();
来访问form2窗体做测试。
运行后,在点击按钮,弹出form2没问题,然后关闭form2,form2窗体资源释放。
这样我再点击按钮时,肯定不能弹出form2窗体。
请问一下这个怎么解决?
回复
wt_sanlian 2011-04-20
[Quote=引用 2 楼 matrixcl 的回复:]
楼主提出了一种不错的思路,值得讨论。

但是一般情况下,封闭性好的单例模式实现,应该把构造函数定义为私有,防止调用者绕过.Instance函数new对象。

模板与封闭性似乎不能兼得啊。
[/Quote]

是啊,我也为这个问题想了很多次,但目前还没有好办法...
回复
matrixcl 2011-04-20
楼主提出了一种不错的思路,值得讨论。

但是一般情况下,封闭性好的单例模式实现,应该把构造函数定义为私有,防止调用者绕过.Instance函数new对象。

模板与封闭性似乎不能兼得啊。
回复
smartls 2011-04-20
如果泛型T包含含参数实例构造器,你怎么处理呢?
回复
发帖
C#
创建于2007-09-28

10.5w+

社区成员

.NET技术 C#
申请成为版主
帖子事件
创建了帖子
2011-04-19 06:41
社区公告

让您成为最强悍的C#开发者