Hàm tạo tĩnh hoạt động như thế nào?


82
namespace MyNameSpace
{
    static class MyClass
    {
        static MyClass()
        {
            //Authentication process.. User needs to enter password
        }

        public static void MyMethod()
        {
            //Depends on successful completion of constructor
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            MyClass.MyMethod();
        }
    }
}

Đây là trình tự mà tôi đã giả định

  1. Bắt đầu của hàm tạo tĩnh
  2. Kết thúc hàm tạo tĩnh
  3. Bắt đầu chính
  4. Khởi động MyMethod
  5. Kết thúc chính

Bây giờ trong bất kỳ tình huống nào nếu 4 sẽ bắt đầu trước 2, tôi bị say. Có khả thi không?


8
Đây là câu hỏi java hay c #? Bạn đã đặt cả hai thẻ và tôi không nghĩ rằng thông số kỹ thuật ở hai ngôn ngữ giống nhau.
ARRG

Trong tác phẩm của tôi, công việc này giống nhau cho cả hai .. Nhưng tôi là chàng trai C # .. Sry for that
om471987 22/02/12

4
Java không có hàm tạo tĩnh theo cách tương tự, chỉ có các khối tĩnh để khởi tạo tĩnh. static {// làm gì đó ...}
deraj 22/02/12

2
Cá nhân tôi cảm thấy không thoải mái với bất kỳ hình thức tương tác nào bên trong một hàm tạo tĩnh. Tôi hiểu mục tiêu của bạn (làm cho mọi phương thức trong lớp tĩnh này đợi cho đến khi người dùng được cấp quyền trước khi cho phép nó chạy), nhưng thực sự không thích phương pháp này hoàn thành nó.
Brian

@ Brian: - Ừ ... Bạn đang đúng ... Tôi chỉ làm phân tích .. Cuối cùng tôi chỉ quyết định không xây dựng sử dụng nhưng phương pháp Initialize
om471987

Câu trả lời:


220

Bạn chỉ hỏi một câu hỏi ở đây nhưng có rất nhiều câu hỏi mà bạn nên hỏi, vì vậy tôi sẽ trả lời tất cả.

Đây là trình tự mà tôi đã giả định

  1. Bắt đầu của hàm tạo lớp (còn được gọi là cctor)
  2. Cuối cctor
  3. bắt đầu của Main
  4. bắt đầu của MyMethod

Điều này có chính xác?

Không. Trình tự đúng là:

  1. Bắt đầu cctor cho Chương trình, nếu có. Không có.
  2. Kết thúc cctor cho Chương trình, nếu có. Không có.
  3. Bắt đầu chính
  4. Bắt đầu cctor cho MyClass
  5. Kết thúc cctor cho MyClass
  6. Khởi động MyClass.MyMethod

Nếu có một bộ khởi tạo trường tĩnh thì sao?

CLR được phép thay đổi thứ tự chạy các trình khởi tạo trường tĩnh trong một số trường hợp. Xem trang của Jon về chủ đề này để biết thêm chi tiết:

Sự khác biệt giữa hàm tạo tĩnh và bộ khởi tạo kiểu

Có bao giờ một phương thức tĩnh giống như MyMethodđược gọi trước khi cctor của lớp đó hoàn thành không?

Đúng. Nếu cctor tự gọi MyMethod thì hiển nhiên MyMethod sẽ được gọi trước khi cctor hoàn thành.

Cctor không gọi MyMethod. Có bao giờ một phương thức tĩnh như MyMethodđược gọi trước khi cctor của MyClass hoàn tất không?

Đúng. Nếu cctor sử dụng kiểu khác mà cctor gọi MyMethod thì MyMethod sẽ được gọi trước khi cctor MyClass hoàn thành.

Không có diễn viên nào gọi MyMethod, trực tiếp hay gián tiếp! Bây giờ có khi nào một phương thức tĩnh như MyMethodđược gọi trước khi cctor của MyClass hoàn tất không?

Không.

Điều đó vẫn đúng ngay cả khi có nhiều chủ đề liên quan?

Đúng. Cctor sẽ kết thúc trên một luồng trước khi phương thức tĩnh có thể được gọi trên bất kỳ luồng nào.

Có thể gọi cctor nhiều hơn một lần không? Giả sử cả hai luồng đều làm cho cctor được chạy.

Cctor được đảm bảo sẽ được gọi nhiều nhất một lần, bất kể có bao nhiêu luồng tham gia. Nếu hai luồng gọi MyMethod "cùng một lúc" thì chúng sẽ chạy đua. Một trong số họ thua cuộc đua và bị chặn cho đến khi cctor MyClass hoàn thành trên chuỗi chiến thắng.

Các khối luồng bị mất cho đến khi cctor được thực hiện? Thật không?

Có thật không.

Vì vậy, điều gì sẽ xảy ra nếu cctor trên chuỗi thắng cuộc gọi mã chặn trên một ổ khóa mà chuỗi thua đã lấy trước đó ?

Sau đó, bạn có một điều kiện đảo ngược thứ tự khóa cổ điển. Chương trình của bạn bế tắc. Mãi mãi.

Điều đó có vẻ nguy hiểm. Làm thế nào tôi có thể tránh khỏi bế tắc?

Nếu bạn cảm thấy đau khi làm điều đó thì hãy ngừng làm điều đó . Đừng bao giờ làm điều gì đó có thể gây tắc nghẽn trong cctor.

Có nên dựa vào ngữ nghĩa khởi tạo cctor để thực thi các yêu cầu bảo mật phức tạp không? Và có phải là một ý kiến ​​hay nếu có một cctor tương tác với người dùng?

Không phải là những ý tưởng tốt. Lời khuyên của tôi là bạn nên tìm một cách khác để đảm bảo rằng các điều kiện tiên quyết ảnh hưởng đến bảo mật của các phương pháp của bạn được đáp ứng.


5
Eric, tôi tò mò tại sao bạn lại thay thế "hàm tạo tĩnh" bằng "hàm tạo lớp" hoặc "cctor" trong câu trả lời này. Sử dụng "hàm tạo tĩnh" khi tham chiếu đến cctor có không đúng không?
phoog

6
@phoog: Tôi muốn nhất quán trong cách sử dụng thuật ngữ của mình, vì vậy tôi đã chọn từ ngắn nhất. "Hàm tạo tĩnh" và "hàm tạo lớp" đều tốt. Là một chi tiết thực thi, phương thức khởi tạo tĩnh của một kiểu được phát ra dưới dạng một phương thức đặc biệt được gọi là ".cctor", vì vậy người ta thường gọi một phương thức khởi tạo như vậy là "một cctor". Nếu tôi viết trong một bối cảnh trang trọng hơn, tôi sẽ sử dụng một trong những thuật ngữ dài hơn.
Eric Lippert

@EricLippert Điều này có đúng với các lớp không tĩnh với một hàm tạo tĩnh không?
Legends

2
@Leosystem: Điều này cũng đúng với các lớp không tĩnh với một hàm tạo tĩnh? Đúng.
Eric Lippert

24

Theo MSDN , một hàm tạo tĩnh:

Một phương thức khởi tạo tĩnh được gọi tự động để khởi tạo lớp trước khi thể hiện đầu tiên được tạo hoặc bất kỳ thành viên tĩnh nào được tham chiếu.

Vì vậy hàm tạo tĩnh sẽ được gọi trước khi phương thức tĩnh MyClass.MyMethod()được gọi (giả sử tất nhiên cũng không được gọi trong quá trình xây dựng tĩnh hoặc khởi tạo trường tĩnh).

Bây giờ, nếu bạn đang làm bất kỳ điều gì không đồng bộ trong đó static constructor, thì nhiệm vụ của bạn là đồng bộ hóa điều đó.


7
Nếu bạn đang làm bất kỳ điều gì không đồng bộ liên quan đến một luồng thứ hai trong một hàm tạo tĩnh, bạn đang ở trong một thế giới đau đớn . Không có gì làm cho bế tắc nhanh hơn. Xem stackoverflow.com/a/8883117/88656 để làm ví dụ.
Eric Lippert

@Eric: đã đồng ý ... Tôi sẽ không muốn làm điều đó, nhưng không chắc mẫu của anh ấy chính xác là anh ấy muốn hoàn thành điều gì vào thời điểm MyMethod được gọi ...
James Michael Hare

11

# 3 thực sự là # 1: khởi tạo tĩnh không bắt đầu cho đến khi sử dụng lần đầu tiên lớp mà nó thuộc về.

Có thể nếu MyMethodđược gọi từ phương thức khởi tạo tĩnh hoặc khối khởi tạo tĩnh. Nếu bạn không gọi MyMethodtrực tiếp hoặc gián tiếp từ phương thức khởi tạo tĩnh của mình, bạn sẽ ổn.


Cũng như một lưu ý, tôi hiểu rằng quá trình statickhởi tạo thực sự có thể được gọi trước khi sử dụng lần đầu tiên tùy thuộc vào tính đủ điều kiện để tối ưu hóa.
James Michael Hare


1
Đối với một phương thức khởi tạo tĩnh, true, nhưng đối với khởi tạo tĩnh là quan điểm của tôi. Xin lỗi, có lẽ tôi chỉ chọn nits trong cụm từ 'static khởi tạo không bắt đầu ...' điều đó đúng với cấu trúc tĩnh, nhưng không phải nếu lớp không có hàm tạo tĩnh, thì khởi tạo tĩnh có thể xảy ra trước đó.
James Michael Hare

Xin lỗi, có lẽ tôi chỉ đang phân tích xung quanh. Trong bối cảnh câu hỏi là hoàn toàn đúng, tôi chỉ lo lắng về câu đó như một câu lệnh độc lập để khởi tạo tĩnh trong ngữ cảnh của các lớp không có hàm tạo tĩnh rõ ràng.
James Michael Hare

@James: Bạn không phân tích bao quát - thuật ngữ là sự khác biệt quan trọng ở đây. Các hàm tạo tĩnh là một khái niệm C #, trong khi khởi tạo kiểu là một thứ .NET. Mã bên trong một phương thức khởi tạo tĩnh (C #) trở thành một phần của bộ khởi tạo kiểu (.NET), nhưng khi nàocách thức mà bộ khởi tạo kiểu kích hoạt (tức là beforefieldinitngữ nghĩa) được xác định bởi lớp C # có một phương thức khởi tạo tĩnh hay không.
LukeH

9

Từ tài liệu (tôi nhấn mạnh):

Một phương thức khởi tạo tĩnh được gọi tự động để khởi tạo lớp trước khi thể hiện đầu tiên được tạo hoặc bất kỳ thành viên tĩnh nào được tham chiếu .


2

Bạn có thể đảm bảo 4 sẽ luôn đến sau 2 (nếu bạn không tạo một thể hiện của lớp từ phương thức tĩnh của mình), tuy nhiên điều này không đúng với 1 và 3.


2

Hàm tạo tĩnh sẽ được gọi trước khi mymethod được thực thi. Tuy nhiên, nếu bạn bị rối nếu 4 được gọi trước 2 thì tôi khuyên bạn nên suy nghĩ lại thiết kế của mình. Không nên làm những thứ phức tạp trong một phương thức khởi tạo tĩnh.


2

CLR đảm bảo rằng hàm tạo tĩnh chạy trước khi bất kỳ thành viên tĩnh nào được truy cập. Tuy nhiên, thiết kế của bạn hơi nặng mùi. Sẽ đơn giản hơn nếu làm điều gì đó như sau:

static void Main(string[] args) 
{ 
     bool userIsAuthenticated = MyClass.AuthenticateUser();
     if (userIsAuthenticated)
         MyClass.MyMethod(); 
 } 

Với thiết kế của bạn, nếu xác thực không thành công, cách duy nhất để ngăn MyMethod chạy là ném một ngoại lệ.


2

Nó đảm bảo rằng phương thức khởi tạo của một lớp tĩnh đã được gọi trước khi bất kỳ phương thức nào của nó được thực thi. Thí dụ:

class Program
{
    static void Main(string[] args)
    {
        Console.WriteLine("Press enter");
        Console.ReadLine();
        Boop.SayHi();
        Boop.SayHi();
        Console.ReadLine();
    }

}

static class Boop
{
    static Boop()
    {
        Console.WriteLine("Hi incoming ...");
    }

    public static void SayHi()
    {
        Console.WriteLine("Hi there!");
    }
}

Đầu ra:

Nhấn Enter

// sau khi nhấn enter

Chào bạn đến ...

Xin chào!

Xin chào!


sử dụng Hệ thống; không gian tên MyNameSpace {class Program {static void Main (string [] args) {Console.WriteLine ("Đã nhập vào main"); Boop.SayHi (); Boop.SayHi (); }} static class Boop {static Boop () {Console.Read (); Console.WriteLine ("Đã nhập khóa mã lệnh"); } public static void SayHi () {Console.WriteLine ("Phương thức được gọi"); }}} vâng chương trình này giúp bạn hiểu rõ hơn
om471987 22/02/12

Có khả năng. Tuy nhiên, lần sau hãy đăng nó như một câu trả lời. Nó hữu ích hơn và hiển thị sau đó.
haiyyu 22/02/12

1

Đây là thứ tự thực tế mà mọi thứ đi xuống:

  1. Bắt đầu Main
  2. Bắt đầu tĩnh MyClass constructor
  3. Kết thúc tĩnh MyClass constructor
  4. Bắt đầu MyMethod
  5. Cuối của Main

0

Hoặc bạn có thể bước qua trình gỡ lỗi.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.