2013-03-06 17 views
8

Trước hết, tôi nhận được câu trả lời trong số What is the use of static constructors?, nhưng tôi muốn có câu trả lời trong ngữ cảnh này.Tại sao chúng ta cần các nhà thầu tĩnh?

Dưới đây là C# tĩnh lớp học của tôi:

public static class BasicClass 
{ 
    static int i = 0; 
    static BasicClass() 
    { 
     i = 10; 
    } 

    public static void Temp() 
    { 
     //some code 
    } 


    public static void Temp1() 
    { 
     //some code 
    } 
} 

Bên này tôi có một tĩnh biến i đó được khởi tạo tới 10 khi nó lần đầu tiên được gọi. Vì vậy, về cơ bản nó có thể là mục đích của một constructor tĩnh nhưng cùng một điều có thể đạt được mà không khai báo một constructor tĩnh bằng cách khởi tạo static int i = 10 phục vụ cùng một mục đích được khởi tạo chỉ một lần.

Sau đó, tại sao chúng ta cần một hàm tạo tĩnh? Hoặc tôi hoàn toàn sai trong việc hiểu khái niệm hoặc sử dụng các nhà thầu tĩnh?

+2

Điều gì sẽ xảy ra nếu chương trình của bạn sử dụng tệp cấu hình? Bạn sẽ muốn lưu trữ cấu hình của mình trong các biến tĩnh, nhưng trước tiên bạn sẽ phải đọc cấu hình từ tệp và nếu tệp không tồn tại (hoặc bạn không thể mở nó vì một số lý do khác), bạn sẽ muốn đặt những cấu hình đó thành giá trị mặc định của chúng. Đó là những gì một constructor tĩnh ** có thể ** được sử dụng cho. – Nolonar

Trả lời

22

Nếu bạn biên dịch lớp đó thành một hội đồng, sau đó sử dụng ILSpy hoặc tương tự để tháo rời kết quả, bạn sẽ nhận thấy rằng tất cả khởi tạo thành viên tĩnh được thực hiện trong hàm dựng tĩnh.

Ví dụ, C# mã sau:

public static class BasicClass 
{ 
    static int i = 10; 
} 

Sẽ tạo ra IL tương đương với:

public static class BasicClass 
{ 
    static int i; 

    static BasicClass() 
    { 
     i = 10; 
    } 
} 

Nói cách khác, khởi tạo trực tiếp chỉ đường cú pháp được cung cấp bởi các biên dịch C#. Dưới mui xe, một hàm tạo tĩnh vẫn được triển khai.

+0

Trong trường hợp đó, nó sẽ không đủ nếu C# hỗ trợ khởi tạo thành viên tĩnh (mà sẽ thực sự được biên dịch thành một hàm dựng tĩnh) nhưng không phải là các hàm tạo tĩnh thực sự? – svick

+1

@svick, có thể được cho là phản tác dụng. Đôi khi bạn phải thực hiện các hoạt động ít tầm thường hơn khởi tạo thành viên (ví dụ: các vòng lặp và các nhánh đơn giản) trên toàn cầu cho một lớp. Tại sao loại bỏ hỗ trợ cho một tính năng có sẵn trong CLR và chỉ yêu cầu một hàm tạo tĩnh không bao giờ ném? –

0

Bạn có thể khởi tạo các trường chỉ bằng các hằng số thời gian biên dịch (trường hợp của bạn). Nhưng trong hàm tạo tĩnh, bạn có thể thực thi một số mã (ví dụ: đọc tệp cấu hình).

1

Bạn không cần hàm tạo tĩnh trong trường hợp này.

static BasicClass() 
{ 
    i = 10; 
} 

static int i = 10; 

có chức năng giống hệt nhau.

6

Vâng, trong ví dụ ví dụ của bạn không thực sự cần thiết, nhưng hãy tưởng tượng khi nào giá trị i phải được đọc từ cơ sở dữ liệu, tệp văn bản hoặc bất kỳ tài nguyên nào khác? Bạn có thể cần một cái gì đó như:

static BasicClass() 
{ 
    using (SomeConnection con = Provider.OpenConnection()) 
    { 
     try 
     { 
      // Some code here 
     } 
     catch 
     { 
      // Handling expeptions, setting default value 
      i = 10; 
     } 
    } 
} 

Bây giờ nó không thể khai báo và khởi tạo lĩnh vực tĩnh của bạn, bạn đang phục vụ tốt hơn sử dụng một constructor tĩnh

0

Một constructor tĩnh không chỉ phục vụ mục đích các biến intializing, nhưng cũng tạo các đối tượng có thể cần thiết cho lớp (hoặc môi trường) tĩnh của bạn để làm việc và gọi các phương thức trên các đối tượng đó hoặc chính lớp của bạn.

1

Câu trả lời cũng là trong câu hỏi liên kết của bạn:

[...] hữu ích đặc biệt cho việc đọc dữ liệu cấu hình cần thiết vào các lĩnh vực readonly vv

Nó được chạy tự động bởi thời gian chạy lần đầu tiên cần thiết (các quy tắc chính xác có> phức tạp (xem "beforefieldinit") và thay đổi một cách tinh tế giữa CLR2 và CLR4). > Trừ khi bạn lạm dụng phản ánh, nó được đảm bảo để chạy nhiều nhất một lần (ngay cả khi hai chủ đề> đến cùng một lúc).

Bạn có thể khởi tạo nhiều thứ phức tạp hơn trong một hàm tạo tĩnh, như thiết lập kết nối cơ sở dữ liệu, v.v. Nếu nó có ý nghĩa là một điều khác ...

1

Một contructor tĩnh có ý nghĩa nếu bạn cần thực hiện một số hành động bên trong hàm tạo và bạn muốn một cá thể duy nhất trong ứng dụng. Ví dụ:

public static class BasicClass 
{ 
    static MyConfiguration _myConfig; 

    static BasicClass() 
    { 
     // read configuration from file 
     _myConfig = ReadConfigFromConfigFile("somefile.conf"); 
    } 

    private static MyConfiguration ReadConfigFromConfigFile(string file) 
    { 
     using (StreamReader reader = new StreamReader(file); 
     { 
     ... 
     } 
    } 
} 

Trong trường hợp bạn giải thích, bạn không cần một hàm tạo tĩnh một cách rõ ràng.

Ngoài ra, bạn có thể áp dụng singleton pattern để đạt được mục đích này.

+3

-1, các hàm tạo tĩnh không thể có tham số. –

+5

Thật nguy hiểm khi thực hiện việc này trong một nhà xây dựng tĩnh. Nếu khởi tạo ném một ngoại lệ chưa được bắt thì không có cách nào dễ dàng để bắt nó, và kiểu sau đó có thể * không bao giờ * được nạp vào appdomain đó. –

0

Một yếu tố chưa được đề cập là có sự khác biệt ngữ nghĩa giữa việc có một hàm tạo tĩnh (được viết bằng cú pháp hàm dựng) gọi foo() so với việc khởi tạo trường tĩnh làm như vậy. Đặc biệt, nếu một kiểu có một hàm dựng tĩnh được viết bằng cách sử dụng cú pháp hàm dựng, thì hàm tạo đó được đảm bảo được gọi mã lần đầu tiên sẽ "sử dụng" kiểu đó được thực hiện; nó sẽ không được gọi trước đó, cũng như nó sẽ không được gọi nếu kiểu không được sử dụng. Ngược lại, mặc dù .NET đảm bảo rằng các trình khởi tạo trường tĩnh của một loại sẽ chạy trước khi bất kỳ trường tĩnh nào của nó được truy cập và sẽ không chạy nhiều lần, .NET là miễn phí để chạy các trình khởi tạo tĩnh như vậy lần đầu tiên nó nghĩ loại có thể sử dụng. Hãy xem xét ví dụ:

if (someCondition()) 
    for (i=0; i<100000000; i++) 
    someClass.someStaticField++; 

Nếu khi trình biên dịch Just-In-Time gặp đoạn code trên, someClass chưa bao giờ được sử dụng, và nó đã một constructor tuyên bố sử dụng cú pháp constructor tĩnh, kết quả mã máy sẽ là một cái gì đó như :

if (someCondition()) 
    for (i=0; i<100000000; i++) 
    { 
    if (someClass.hasBeenInitialized) 
     someClass.runConstructor(); 
    someClass.someStaticField++; 
    } 

thực hiện kiểm tra nếu trong vòng vòng lặp. Ngược lại, nếu đã có khởi tạo trường nhưng không có khai báo kiểu hàm dựng, thì JIT có khả năng thực hiện việc xây dựng tĩnh someClass trước khi chạy mã, do đó loại bỏ sự cần thiết phải kiểm tra if bên trong nó.