單例模式(20)

来源:http://www.cnblogs.com/xiaomowang/archive/2017/02/13/6392954.html
-Advertisement-
Play Games

今天我們來講一下單例模式,下麵我們來用winform來做一個簡單的展示,就是點擊一個菜單,彈出另一個窗體(做成父子窗體的形式)。 建一個窗體(父窗體),拖一個MenuStrip,再建一個窗體(子窗體)。 然後: 現在,我們看一下執行結果。 我們可以看到,每次我們點擊一下工具,都會彈出一個新的窗體,我 ...


今天我們來講一下單例模式,下麵我們來用winform來做一個簡單的展示,就是點擊一個菜單,彈出另一個窗體(做成父子窗體的形式)。

建一個窗體(父窗體),拖一個MenuStrip,再建一個窗體(子窗體)。

然後:

 1         private void Form1_Load(object sender, System.EventArgs e)
 2         {
 3             this.IsMdiContainer = true;
 4         }
 5 
 6         private void 工具欄ToolStripMenuItem_Click(object sender, System.EventArgs e)
 7         {
 8             Form2 form2 = new Form2();
 9             form2.MdiParent = this;
10             form2.Show();
11         }

現在,我們看一下執行結果。

我們可以看到,每次我們點擊一下工具,都會彈出一個新的窗體,我們想要的結果是:之彈出一次這個窗體就行。

有些伙伴說,可以用ShowDialog() 啊,在此,說明一下,在父子窗體中,子窗體是不能用ShowDialog()出來的,再退一步講,即便是能用ShowDialog(),但是這是一個阻塞機制,很不靈活。

那麼,為了實現我們想要的結果,我們該如何做呢?

簡單啊,我們只需要修改一下點擊事件里的代碼就可以了。

先聲明一個子窗體的全局變數,然後修改一下代碼:

 1         private Form2 form2;
 2         private void Form1_Load(object sender, System.EventArgs e)
 3         {
 4             this.IsMdiContainer = true;
 5         }
 6 
 7         private void 工具欄ToolStripMenuItem_Click(object sender, System.EventArgs e)
 8         {
 9             if (form2 == null)
10             {
11                 form2 = new Form2();
12                 form2.MdiParent = this;
13                 form2.Show();
14             }
15         }

這樣就可以實現我們想要的結果了,這樣就完了嘛?

這裡存在兩個問題:

1、如果我有很多按鈕,每個按鈕都想彈出這個子窗體,想實現這個結果,需要複製粘貼這些代碼,顯然是很失敗的做法。

2、上述結果,如果我關閉了打開的窗體,我在點擊工具菜單,則不會再彈出窗體來了。(因為關閉窗體後,該窗體僅僅是Disposed了,但對象還不是null,所以判斷是否為null是有一定的問題的。)

針對上述兩個問題,我們來改進一下。就用到今天要講的單例模式了,好,我們來看一下如何實現。

Form2中的代碼

 1     public partial class Form2 : Form
 2     {
 3         //聲明一個靜態的類變數
 4         private static Form2 form2 = null;
 5         //構造方法私有,外部代碼不能直接new來實例化它
 6         private Form2()
 7         {
 8             InitializeComponent();
 9         }
10         //得到類實例的方法,返回值就是本類對象,註意也是靜態的。
11         public static Form2 GetForm()
12         {
13             //當內部的form2是null或者被Dispose過,則new它
14             //並且設計其MdiParent為Form1,此時將實例化的對象存在靜態的變數form2中,以後就可以不用實例化而得到它了
15             if (form2 == null || form2.IsDisposed)
16             {
17                 form2 = new Form2();
18                 form2.MdiParent = Form1.ActiveForm;
19             }
20             return form2;
21         }
22     }

Form1 中的調用

 1     public partial class Form1 : Form
 2     {
 3        
 4         public Form1()
 5         {
 6             InitializeComponent();
 7         }
 8     
 9         private void Form1_Load(object sender, System.EventArgs e)
10         {
11             this.IsMdiContainer = true;
12         }
13         private void 工具欄ToolStripMenuItem_Click(object sender, System.EventArgs e)
14         {
15             Form2.GetForm().Show();
16         }
17     }

這樣,就達到了我們想要的效果了。

我們來總結一下:

單例模式:保證一個類僅有一個實例,並提供一個訪問它的全局訪問點。

通常我們剋讓一個全局變數是的一個對象被訪問,但它不能防止你實例化多個對象。最好的辦法就是,讓類自身負責保存它的唯一實例,這個類可以保證沒有其他實例可以被創建,並且他可以提供一個訪問該實例的方法。

好,我們來寫一下單例模式的代碼

 1     class Singleton
 2     {
 3         private static Singleton instance;
 4         //構造方法讓其private,這就堵死了外界利用用new創建次類實例的可能
 5         private Singleton()
 6         {
 7             
 8         }
 9         //此方法是獲得本類實例的唯一全局訪問點
10         public static Singleton GetInstance()
11         {
12             //若實例不存在,則new一個新實例,否則返回已有的實例
13             if (instance==null)
14             {
15                 instance = new Singleton();
16             }
17             return instance;
18         }
19     }

客戶端:

 1        public static void Main()
 2         {
 3             Singleton s1 = Singleton.GetInstance();
 4             Singleton s2 = Singleton.GetInstance();
 5             //比較兩次實例化後對象的結果是否相同
 6             if (s1==s2)
 7             {
 8                 Console.WriteLine("兩個對象是相同的實例。");
 9             }
10             Console.ReadKey();
11         }

另外還有一個問題,如果多線程中,多個線程同時訪問Singleton類,調用GetInstance()方法,會有可能創造多個實例的。所以,我們可以用lock進行處理一下。

好,我們來看用lock處理後的Singleton類

 1     class Singleton
 2     {
 3         private static Singleton instance;
 4         //程式運行時創建一個靜態只讀的進程輔助對象
 5         private static readonly object syncRoot = new object();
 6         //構造方法讓其private,這就堵死了外界利用用new創建次類實例的可能
 7         private Singleton()
 8         {
 9 
10         }
11         //此方法是獲得本類實例的唯一全局訪問點
12         public static Singleton GetInstance()
13         {
14             //在同一個時刻加了鎖的那部分程式只有一個線程可以進入
15             lock (syncRoot)
16             {
17                 //若實例不存在,則new一個新實例,否則返回已有的實例
18                 if (instance == null)
19                 {
20                     instance = new Singleton();
21                 }
22             }
23             return instance;
24         }
25     }

對於上述的代碼,小伙伴們發現了一個問題沒有,就是不管instance是不是為null,都會先加鎖,這勢必會影響性能的,好我們來看一下優化後的:

 1     class Singleton
 2     {
 3         private static Singleton instance;
 4         //程式運行時創建一個靜態只讀的進程輔助對象
 5         private static readonly object syncRoot = new object();
 6         //構造方法讓其private,這就堵死了外界利用用new創建次類實例的可能
 7         private Singleton()
 8         {
 9 
10         }
11         //此方法是獲得本類實例的唯一全局訪問點
12         public static Singleton GetInstance()
13         {
14             //先判斷實例是否存在,如果不存在再加鎖處理
15             if (instance==null)
16             {
17                 //在同一個時刻加了鎖的那部分程式只有一個線程可以進入
18                 lock (syncRoot)
19                 {
20                     //若實例不存在,則new一個新實例,否則返回已有的實例
21                     if (instance == null)
22                     {
23                         instance = new Singleton();
24                     }
25                 } 
26             }
27             return instance;
28         }
29     }

其實再實際應用當中,C#與用功語言運行庫也提供了一種“靜態初始化”方法,這種方法不需要開發人員顯示的編寫線程安全代碼,即可解決多線程環境下他是不安全的問題。

好,下麵我們來看一下“靜態初始化”方法的單例模式

 1     //sealed阻止發生派生,而派生可能會增加實例
 2     public sealed class Singleton
 3     {
 4         //在第一次引用類的任何成員時創建實例,共功與原運行庫負責處理變數初始化。
 5         private static readonly Singleton instance = new Singleton();
 6         private Singleton()
 7         {
 8         }
 9 
10         public static Singleton GetInstance()
11         {
12             return instance;
13         }
14     }

這種靜態初始化的方式是自己被載入時就將自己實例化,所以被形象的成為惡漢式單例類,原先的單例模式處理是要再第一次被引用時,才會將自己實例化,所以被稱為懶漢單例類。

好,單例模式我們就介紹完了,下一篇博文我們講 橋接模式


本系列將持續更新,喜歡的小伙伴可以點一下關註和推薦,謝謝大家的支持


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 歡迎大家來咨詢海南七星彩打獎系統,系統可以支持手機下註的南方海南,湛江七星彩投註網站系統,也可以出租,出售等渠道,或者定製,可以選擇支持手機下註或者不支持,需要定製的,可以私信,扣扣:1930-1335-70 本截圖只作為演示,如有需要定製,購買,請聯繫客服購買正版授權使用。 會員演示圖: 代理演示 ...
  • 1 import java.util.*; 2 class CalendarTest 3 { 4 /*先輸出提示語句,並接受用戶輸入的年、月。 5 根據用戶輸入的年,先判斷是否是閏年。 6 根據用戶輸入的年份來判斷月的天數。 7 用迴圈計算用戶輸入的年份距1900年1月1日的總天數。 8 用迴圈計算... ...
  • mybatis和hibernate之間的對比。及應用場景的介紹 ...
  • sso單點登錄系統的最全的搭建方法,只要你按我的步驟來,就可以成功的搭建出你的sso單點登錄系統。 ...
  • 定義 觀察者模式(有時又被稱為發佈(publish)-訂閱(Subscribe)模式,在此種模式中,一個目標物件管理所有相依於它的觀察者物件,並且在它本身的狀態改變時主動發出通知。這通常透過呼叫各觀察者所提供的方法來實現。此種模式通常被用來實現事件處理系統(摘自百度百科)。 關鍵詞:發佈-訂閱 為什 ...
  • 1.定義 跨域是指a頁面想獲取b頁面資源,如果a、b頁面的協議、功能變數名稱、埠、子功能變數名稱不同,所進行的訪問行動都是跨域的,而瀏覽器為了安全問題一般都限制了跨域訪問,也就是不允許跨域請求資源。註意:跨域限制訪問,其實是瀏覽器的限制。理解這一點很重要!!! 2.跨域訪問示例 假設有兩個網站,A網站部署在:ht ...
  • 一、封裝: 封裝是實現面向對象程式設計的第一步,封裝就是將數據或函數等集合在一個個的單元中(我們稱之為類)。被封裝的對象通常被稱為抽象數據類型。 封裝的意義: 封裝的意義在於保護或者防止代碼(數據)被我們無意中破壞。在面向對象程式設計中數據被看作是一個中心的元素並且和使用它的函數結合的很密切,從而保 ...
  • 如果一個方法中包含多個布爾類型的參數,一是方法不容易理解,二是調用時容易出錯。 重構前代碼 重構後代碼 重構後,將原來方法改為private防止外部調用,而暴露出命名良好的方法供調用。 ...
一周排行
    -Advertisement-
    Play Games
  • 示例項目結構 在 Visual Studio 中創建一個 WinForms 應用程式後,項目結構如下所示: MyWinFormsApp/ │ ├───Properties/ │ └───Settings.settings │ ├───bin/ │ ├───Debug/ │ └───Release/ ...
  • [STAThread] 特性用於需要與 COM 組件交互的應用程式,尤其是依賴單線程模型(如 Windows Forms 應用程式)的組件。在 STA 模式下,線程擁有自己的消息迴圈,這對於處理用戶界面和某些 COM 組件是必要的。 [STAThread] static void Main(stri ...
  • 在WinForm中使用全局異常捕獲處理 在WinForm應用程式中,全局異常捕獲是確保程式穩定性的關鍵。通過在Program類的Main方法中設置全局異常處理,可以有效地捕獲並處理未預見的異常,從而避免程式崩潰。 註冊全局異常事件 [STAThread] static void Main() { / ...
  • 前言 給大家推薦一款開源的 Winform 控制項庫,可以幫助我們開發更加美觀、漂亮的 WinForm 界面。 項目介紹 SunnyUI.NET 是一個基於 .NET Framework 4.0+、.NET 6、.NET 7 和 .NET 8 的 WinForm 開源控制項庫,同時也提供了工具類庫、擴展 ...
  • 說明 該文章是屬於OverallAuth2.0系列文章,每周更新一篇該系列文章(從0到1完成系統開發)。 該系統文章,我會儘量說的非常詳細,做到不管新手、老手都能看懂。 說明:OverallAuth2.0 是一個簡單、易懂、功能強大的許可權+可視化流程管理系統。 有興趣的朋友,請關註我吧(*^▽^*) ...
  • 一、下載安裝 1.下載git 必須先下載並安裝git,再TortoiseGit下載安裝 git安裝參考教程:https://blog.csdn.net/mukes/article/details/115693833 2.TortoiseGit下載與安裝 TortoiseGit,Git客戶端,32/6 ...
  • 前言 在項目開發過程中,理解數據結構和演算法如同掌握蓋房子的秘訣。演算法不僅能幫助我們編寫高效、優質的代碼,還能解決項目中遇到的各種難題。 給大家推薦一個支持C#的開源免費、新手友好的數據結構與演算法入門教程:Hello演算法。 項目介紹 《Hello Algo》是一本開源免費、新手友好的數據結構與演算法入門 ...
  • 1.生成單個Proto.bat內容 @rem Copyright 2016, Google Inc. @rem All rights reserved. @rem @rem Redistribution and use in source and binary forms, with or with ...
  • 一:背景 1. 講故事 前段時間有位朋友找到我,說他的窗體程式在客戶這邊出現了卡死,讓我幫忙看下怎麼回事?dump也生成了,既然有dump了那就上 windbg 分析吧。 二:WinDbg 分析 1. 為什麼會卡死 窗體程式的卡死,入口門檻很低,後續往下分析就不一定了,不管怎麼說先用 !clrsta ...
  • 前言 人工智慧時代,人臉識別技術已成為安全驗證、身份識別和用戶交互的關鍵工具。 給大家推薦一款.NET 開源提供了強大的人臉識別 API,工具不僅易於集成,還具備高效處理能力。 本文將介紹一款如何利用這些API,為我們的項目添加智能識別的亮點。 項目介紹 GitHub 上擁有 1.2k 星標的 C# ...