Singleton 單件模式 泛型 (無法訪問已釋放的對象)

來源:互聯網
上載者:User

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

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.