C#/VB.NET 物件導向技巧研究(二)_靜態資料屬性、靜態方法/函數/建構子、公用工具類

靜態的觀念白話層面來看

無非就是不需要先實體化物件個體
直接透過該類去調用相應的資料屬性欄位、方法
並且具內存共享的機制

因此又有一種說法就是稱為類別層級的屬性、方法、函數....
甚至常講到的靜態類別(不需要實體化!!!!)




於Visual Studio中
View -> Object Browser選單去點選 mscorlib.dll 中的 System命名空間
就可以看到常用的Console , Math , Environment....etc都是用static修飾的類別

代表直接透過該類去直接呼叫相關方法而不用再實體化
這些就是俗稱的公用工具類





想想你是負責維護一套金融系統的工程師

當中想必一定會看到類似這種物件藍圖
負責處理儲蓄帳戶相關業務流程

每個用戶都要對應餘額
在一般情況下大家都有統一的利率

CODE
Main


 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
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace MyOOP2
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("static data ");
            SavingAccount s1 = new SavingAccount(50);
            SavingAccount s2 = new SavingAccount(100);
            Console.WriteLine("Interest Rate is: {0}" , SavingAccount.GetInterestRate());

            SavingAccount s3 = new SavingAccount(1000.75);
            Console.WriteLine("Interest Rate is: {0}", SavingAccount.GetInterestRate());


            Console.ReadKey();
        }
    }
}

SavingAccount Class


 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
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace MyOOP2
{
    class SavingAccount
    {
        //實體層級(非靜態)資料
        public double curBalance;//餘額
        //類別層級(靜態)資料->同一類別之所有物件會共享內存
        //前提:所有儲蓄帳戶利率皆相同的情況
        public static double curInterestRate = 0.04;

        public SavingAccount(double balance)
        {
            curBalance = balance;
        }

        //獲得利率屬性之靜態方法
        public static double GetInterestRate()
        {
            return curInterestRate;
        }
        //設定利率屬性之靜態方法
        public static void SetInterestRate(double newRate)
        {
            curInterestRate = newRate;
        }

    }
}

因此不管之後又有多少人申辦新帳戶
利率都是固定不變而且是共享的(只要是這個類別所建立出來的實體)
因為於.NET 底層CLR內存分配時對於靜態資料不會重置
舉凡是靜態修飾內容內存分配僅進行一次


何時我們比較適合用static來修飾???

以此設計案例來看
如果你不將利率設為靜態
則每當有新用戶申辦時你都要再次重新設定依次利率
當有一萬個客戶時這將是一大災難
畢竟不會有人會喜歡重複調用SetInterestRate這麼多次



靜態建構子


在之前建構子單元介紹的相關概念
皆是屬於非靜態(物件層級)的建構子觀念

這裡我們來觀察看看
於剛剛的類別中所定義的實體層級的建構子去賦予值給靜態資料成員
會發生捨麼事情



每當實體化新物件則利率就會被刷新
因此SetInterestRate方法相當於無效了
也違背了起初設置為靜態的用意
會被多次分配重置內存!!!!!!

OK 所以我們只要確保不要在物件層級的建構子中做賦予靜態資料值的事情就好了??
但是若真的有靜態資料是需要在運行時獲得的情況呢??

比方一家銀行利率是變動的
需透過資料庫或者外部檔案進行讀取設置時
此時是不太能夠寫死的

此時我們仍然需要利用建構子於實體化時賦予
差異在於這裡需要用到靜態建構子
主要也是用static修飾


適合用在編譯初始化階段未知的靜態資料內容




這裡和剛剛差異在於你會發現輸出位於static 建構子的辨別訊息只出現一次
代表只會運行該部分一次

如此一來就能確保CLR於第一次調用對於靜態資料確實僅分配第一次內存
並不會隨之後的物件實體化而又在調用

總結:
一個類別中止渴定義一個靜態建構子(換言之,靜態建構子不允許overloading)
靜態建構子不允許再添加其餘訪問修飾字
無論實體化多少物件,靜態建構子只會運行一次
於執行物件實體化程式語句或首次訪問靜態成員之前都會先呼叫一次靜態建構子
(換言之,靜態建構子執行順序優先於任何物件層級建構子)

CODE

Main


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace MyOOP2
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("static data ");
            SavingAccount s1 = new SavingAccount(50);
            Console.WriteLine("Interest Rate is: {0}", SavingAccount.GetInterestRate());
            SavingAccount.SetInterestRate(0.08);

            SavingAccount s2 = new SavingAccount(100);
            Console.WriteLine("Interest Rate is: {0}" , SavingAccount.GetInterestRate());
            Console.ReadKey();
        }
    }
}


SavingAccount


 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
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace MyOOP2
{
    class SavingAccount
    {
        //物件層級(非靜態)資料
        public double curBalance;//餘額
        public static double curInterestRate;

        //物件層級建構子
        public SavingAccount(double balance)
        {
            Console.WriteLine("In object ctor");
            //curInterestRate = 0.04;  //Not Good 
            curBalance = balance;
        }

        static SavingAccount()
        {
            Console.WriteLine("In static ctor");
            curInterestRate = 0.04;
        }

        //獲得利率屬性之靜態方法
        public static double GetInterestRate()
        {
            return curInterestRate;
        }
        //設定利率屬性之靜態方法
        public static void SetInterestRate(double newRate)
        {
            curInterestRate = newRate;
        }

    }
}



靜態類(公用工具類)

靜態類會直接限定所有成員及方法都要用static修飾
更不能做任何new 實體化的動作

若於定義過程中忘記給予static修飾則會跳出不允許宣告物件實體層級的成員


如下是一個打印當前時間工具類的示範


CODE

TimeUtility


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace MyOOP2
{
    static class TimeUtility
    {
        public static void PrintTime()
        {
            Console.WriteLine(DateTime.Now.ToShortTimeString());
        }

        public static void PrintDate()
        {
            Console.WriteLine(DateTime.Today.ToShortDateString());
        }

    }
}



留言

這個網誌中的熱門文章

何謂淨重(Net Weight)、皮重(Tare Weight)與毛重(Gross Weight)

Architecture(架構) 和 Framework(框架) 有何不同?_軟體設計前的事前規劃的藍圖概念

經得起原始碼資安弱點掃描的程式設計習慣培養(五)_Missing HSTS Header