Static Constructor ใช้สำหรับ Initialize Static Data หรือทำ Action ใดๆ ที่ต้องทำเพียงครั้งเดียวเท่านั้น มันจะถูกเรียกโดยอัตโนมัติ ก่อนที่จะสร้าง Instance แรก หรือมีการอ้างอิง Static Members ใดๆ
Static Constructors มีประโยชน์เมื่อสร้าง Wrapper Classes สำหรับ Unmanaged Code เมื่อ Constructor สามารถเรียกเมธอด LoadLibrary ได้ Static Constructors ยังเป็นสถานที่ที่สะดวกสำหรับบังคับ Run-time Checks บน Type Parameter ที่ไม่สามารถตรวจสอบได้ที่ Compile-time ผ่าน Constraints
คุณสมบัติของ Static Constructors
- Static Constructor ไม่รับ Access Modifiers และไม่มี Parameters
- Class หรือ Struct สามารถมี Static Constructor ได้เพียงตัวเดียว
- Static Constructors ไม่สามารถสืบทอด (Inherit) หรือ Overload ได้
- Static Constructor ไม่สามารถเรียกโดยตรงได้ และมีเจตนาให้ถูกเรียกโดย CLR เท่านั้น เรียกโดยอัตโนมัติ
- User ไม่มี Control ต่อเวลาที่ Static Constructor ถูก Execute ในโปรแกรม
- Static Constructor ถูกเรียกโดยอัตโนมัติเพื่อ Initialize Class ก่อนสร้าง Instance แรก หรืออ้างอิง Static Members Static Constructor จะรันก่อน Instance Constructor Type's Static Constructor ถูกเรียกเมื่อ Static Method ถูก Assign ให้ Event หรือ Delegate ถูก Invoke ไม่ใช่ตอน Assign ถ้ามี Static Field Variable Initializers ในคลาสของ Static Constructor จะถูก Execute ตามลำดับข้อความที่ปรากฏใน Class Declaration ทันทีก่อน Execution ของ Static Constructor
- ถ้าไม่ได้จัดเตรียม Static Constructor เพื่อ Initialize Static Fields ทุก Static Fields จะถูก Initialize เป็น Default Value ตามที่ระบุใน Default values of C# types
- ถ้า Static Constructor โยน Exception Runtime จะไม่ Invoke มันอีกครั้ง และ Type จะยังคง Uninitialized สำหรับ Lifetime ของ Application Domain ที่โปรแกรมรันอยู่
- การมี Static Constructor ป้องกันการเพิ่ม BeforeFieldInit Type Attribute ซึ่งจำกัด Runtime Optimization
- Field ที่ประกาศเป็น
static readonlyอาจถูก Assign เฉพาะตอน Declaration หรือใน Static Constructor เท่านั้น เมื่อไม่จำเป็นต้องมี Explicit Static Constructor ให้ Initialize Static Fields ตอน Declaration แทนที่จะผ่าน Static Constructor เพื่อ Runtime Optimization ที่ดีกว่า
ตัวอย่างพื้นฐาน
using System;
namespace DemoApplication {
public class Program {
static void Main(string[] args) {
Car user = new Car();
Car user1 = new Car();
Console.ReadLine();
}
}
public class Car {
static Car() {
Console.WriteLine("Static constructor called");
}
public Car() {
Console.WriteLine("Default constructor called");
}
}
}
Output
Static constructor called
Default constructor called
Default constructor called
ในตัวอย่างข้างต้น เราเห็นว่า Static Constructor ถูกเรียกเพียงครั้งเดียว
ตัวอย่างการใช้งานจริง: Global Start Time
using System;
using System.Threading;
namespace DemoApplication {
public class Car {
protected static readonly DateTime globalStartTime;
protected int RouteNumber { get; set; }
static Car() {
globalStartTime = DateTime.Now;
Console.WriteLine($"Static constructor called. Global start time: {globalStartTime.ToLongTimeString()}");
}
public Car(int routeNum) {
RouteNumber = routeNum;
Console.WriteLine($"Car {RouteNumber} is created.");
}
public void Drive() {
TimeSpan elapsedTime = DateTime.Now - globalStartTime;
Console.WriteLine($"Car {this.RouteNumber} is starting its route {elapsedTime.Milliseconds} minutes after global start time {globalStartTime.ToShortTimeString()}.");
}
}
class TestCar {
static void Main() {
Car car1 = new Car(1);
Car car2 = new Car(2);
car1.Drive();
Thread.Sleep(25);
car2.Drive();
Console.ReadLine();
}
}
}
Output
Static constructor called. Global start time: 7:09:06 AM
Car 1 is created.
Car 2 is created.
Car 1 is starting its route 25 minutes after global start time 7:09 AM.
Car 2 is starting its route 50 minutes after global start time 7:09 AM.
สรุปเมื่อไหร่ควรใช้ Static Constructor
- Initialize Static Fields ที่ต้องมี Logic ซับซ้อน (ไม่ใช่แค่ค่าคงที่)
- Setup Unmanaged Resources (เช่น LoadLibrary)
- Runtime Validation ของ Generic Type Parameters
- Singleton Pattern Implementation
- Logging/Monitoring การโหลด Type ครั้งแรก