一般在百度搜一下,會出來一下內容,看來很好用。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;
}
這一句了。