【公告】網站目前停止所有的課程訂閱服務,原有學員權益不受影響,造成不便還請見諒,我們正在打造更多課程以及圖書,包含 Python 為主的課程主題,未來將會合併且擴充目前的課程內容,提供全新課程訂閱服務,感謝學員的支持。
【公告】《Entity Framework 實務精要》 絕版優惠學習活動,是針對最後一批少量書籍絕版的短期特別活動,只提供 20 個名額(剩 5 名),教學影片要在今年暑假過後才會重新開放訂閱,並沒有提供之前單獨購書的讀者,還請見諒, 如果您需要教學影片,屆時再請參與,也感謝讀者學員的支持,我們正在開發更多的教學內容,歡迎與我們一起努力。
《Entity Framework 實務精要》 絕版優惠學習活動》

類別靜態成員

靜態成員以 static 關鍵字對其進行宣告,此種類型的成員不需類別實體,直接透過類別名稱以點 . 運算子連接成員名稱進行引用,以下為靜態成員的宣告語法:

static T Smember

其中 static 宣告為靜態成員的關鍵字,而 T 為成員型態, Smember 則是成員命名,例如變數或是方法,一旦以 static 宣告,直接透過類別名稱進行引用即可。

靜態成員最好的例子,是用來作為程式起始點的 Main 方法,在每個新建立的主控台應用程式專案中,會自動產生這個主方法,以 static 關鍵字進行宣告:

static void Main(string[] args)
{
}

由於應用程式是在沒有任何物件被建立的情形下開始,這個方法被宣告為 static ,不需要實體即可執行,啟動應用程式。

以下的 TimeConversion 類別示範靜態成員。

class TimeConversion
{
    private static int hour = 24;
    private static int min = 60;
    public static int HourToMin(int pHour)
    {
        int intMin;
        intMin = pHour * min;
        return intMin;
    }
    public static int DayToHour(int pDay)
    {
        int intHour;
        intHour = pDay * hour;
        return intHour;
    }
}

欄位 hour 代表一天的小時數, min 則代表一小時的分鐘數,這兩個都是固定的數值,以 static 直接宣告為靜態。

接下來的HourToMin 函式與 DayToHour 函式,同樣均是 static ,分別執行小時/分鐘數的轉換與日/時數的轉換。 HourToMin 函式將參數值 pHour 乘上 min ,代表由時數轉為分鐘數, DayToHour 函數則將參數 pDay 乘上 hour ,代表從天數轉換成為小時數。

class Program
{
    static void Main(string[] args)
    {
        int hour;
        int day;
        int min;
        int ahour;

        Console.Write("請輸入小時數:");
        hour = int.Parse(Console.ReadLine());
        Console.Write("請輸入天數:");
        day = int.Parse(Console.ReadLine());

        min = TimeConversion.HourToMin(hour);
        ahour = TimeConversion.DayToHour(day);

        Console.WriteLine("");
        Console.WriteLine("{0} 個小時等於 {1} 分鐘!!", hour, min);
        Console.WriteLine("{0} 天等於 {1} 個小時!!", day, ahour);
        Console.ReadKey();
    }
}

主程式方法 Main 的引用 TimeConversion 類別所定義的靜態方法,由於是靜態成員,因此並不需要建立 TimeConversion 物件。

請輸入小時數:6
請輸入天數:4

6 個小時等於 360 分鐘
4 天等於 96 個小時

由於時間單位的換算邏輯是固定的,對於這些固定的值或運算邏輯,設計為靜態成員是比更合適的選擇,透過相同的類別即可取得需要的結果,反過來說,靜態成員並不會隨著物件的建立而有不同的值,所有類別的靜態成員均是同一個版本,只有不隨物件實體改變的固定資料成員,才需要考慮建立靜態成員,否則實體成員才是正確的選擇。

另外要特別注意,一旦宣告為靜態,其中引用的同樣必須是靜態成員:

class TimeConversion
{
    private int hour = 24;
    private int min = 60;
    public static int HourToMin(int pHour)
    {
        int intMin;
        intMin = pHour * min;
        return intMin;
    }
    public static int DayToHour(int pDay)
    {
        int intHour;
        intHour = pDay * hour;
        return intHour;
    }
}

將其中的欄位,包含 hour 與 min 移除 static 關鍵字宣告,重新執行會出現以下的錯誤訊息:

錯誤 CS0120 需要有物件參考,才可使用非靜態欄位、方法或屬性 'TimeConversion.hour'。
錯誤 CS0120 需要有物件參考,才可使用非靜態欄位、方法或屬性 'TimeConversion.min'。

無論 HourToMin() 或是 DayToHour() ,都是 static ,因此都會出現以上的錯誤訊息。

靜態建構式

在類別物件建立的過程中,如果需要初始化靜態變數,可以透過定義靜態建構式來提供需要的支援,在建構式名稱之前標註 static 即可成為靜態建構式。

public class StaticConstructors
{
    public static readonly string now;
    static Static Constructors()
    {
        now = DateTime.Now.ToString();
        Console.WriteLine("Now:{0}", now);
    }
}     

在這個類別中,變數 now 是靜態的唯讀欄位,於宣告為 static的建構式中將其初始化為建構式執行當下的時間,而為了方便測試說明,最後將其輸出。

靜態建構式在第一次物件建立時被執行,而且僅執行一次,以下嘗試建立兩個 StaticConstructors 物件。

StaticConstructors sc0 = new StaticConstructors();
StaticConstructors sc1 = new StaticConstructors();

當這兩行程式碼執行完畢,會得到以下的輸出結果:

Now:2018/2/13 下午 04:36:03

變數值 now 只經過一次設定並輸出,也就是第一次物件 sc0 建立的時候,接下來建立StaticConstructors 物件 sc1 ,將不會再執行靜態建構式。

另外,如果未建立任何實體物件,而第一次引用靜態成員時,同樣會執行靜態建構式,例如以下這一行程式碼:

string now = StaticConstructors.now;

其中引用了靜態變數 now 取得其值,此時靜態建構式執行,輸出當下的時間。

關於常數 constant

如果類別有宣告為常數(const)的成員,本身即具備靜態特性,不需要再宣告為 static ,直接透過類別名稱進行引用即可。

public class TemperatureConversion
{
    public const double boiling_point = 100.0;
    public const double freezing_point = 0.0;
}

欄位 boiling_point 與 freezing_point 分別表示水的沸點與冰點(攝氏),由於是固定值因此宣告為const ,接下來嘗試讀取欄位值:

double bp = TemperatureConversion.boiling_point;
double fp = TemperatureConversion.freezing_point;
Console.WriteLine("水的沸點是攝氏 {0} 度 ", bp);
Console.WriteLine("水的冰點是攝氏 {0} 度 ", fp);

其中透過類別名稱,直接引用 const 欄位值,並將取得的值輸出。

水的沸點是攝氏 100 度
水的冰點是攝氏 0 度

輸出結果中,可以看到引用 const 欄位的效果。事實上,將一個 const 成員宣告為 static 是不合法的,你不可以宣告如下:

public static const double boiling_point = 100.0 ;

其中的 static 會導以下的錯誤訊息:

錯誤 CS0504 常數 'boiling_point' 不可標記為 static

靜態類別

類別本身亦能直接宣告為靜態,當類別之中沒有資料或是方法成員與類別實體有任何關聯,直接建立靜態類別可以將類別成員與物件的運作完全獨立開來,這特別適合我們用來建立提供整個應用程式所需的公用成員類別。

建立靜態類需以關鍵字static對類別進行宣告:

static class StaticClass
{
// 靜態成員 …

經過 static 修飾, StaticClass 便成為一個靜態類別,設計這個類別的時候必須遵守上述的限制。

static class SecondConversion
{
    public static int HourToSecond(int hour)
    {
        return hour * 60 * 60;
    }
    public static int DayToSecond(int day)
    {
        return day * 60 * 60 * 24;
    }
    public static int MinToSecond(int min)
    {
        return min * 60;
    }
}

SecondConversion 類別宣告為靜態,這是一個轉換時間秒數的類別程式,其中定義了三個靜態方法成員,分別將指定的日、小時與分鐘數,轉換成為對等的秒數回傳。

由於轉換的公式為不變的常理,即使在任何情況下這種規則依然不會改變,因此它們不需要與任何類別實體產生關聯,宣告為 static 可以避免在執行階段產生任何不必要的類別實體。

class Program
{
    static void Main(string[] args)
    {
        int msecond = SecondConversion.MinToSecond(5);
        int hsecond = SecondConversion.HourToSecond(5);
        int dsecond = SecondConversion.DayToSecond(5);

        Console.WriteLine("5分鐘等於 {0} 秒 ", msecond);
        Console.WriteLine("5小時等於 {0} 秒 ", hsecond);
        Console.WriteLine("5日  等於 {0} 秒 ", dsecond);

        Console.ReadLine();
    }
}

一開始直接透過類別名稱SecondConversion 引用靜態成員,取得轉換後的結果輸出。

5分鐘等於 300 秒
5小時等於 18000 秒
5日  等於 432000 秒

設計靜態類別有幾點必須注意,除了只能配置靜態成員,另外本身無法建立其實體物件,例如以下這一行程式碼:

SecondConversion sc = new SecondConversion();

其中會產生以下的錯誤訊息:

錯誤 CS0723 無法宣告靜態類型 'SecondConversion' 的變數
錯誤 CS0712 無法建立靜態類別 'SecondConversion' 的執行個體

.NET 內建的 Math 類別,是一個最典型的靜態類別,進入 Math 類別的規格頁可以看到這個類別的宣告如下:

public static class Math

由於數算運算方法公式均是固定的,例如計算絕對值的 abs() ,或是三角函式的 sin()、cos() 以及 Tan() 等等,因此 Math 類別直接設計成靜態類別可以更方便使用。




沒有留言: