2012-05-05 25 views
18

Câu hỏi của tôi xuất hiện sau khi tái cấu trúc lớp chỉ chứa các phương thức tĩnh được khai báo là lớp static và gặp sự cố lạ khi khởi động ứng dụng.Tiềm năng cạm bẫy với các nhà thầu tĩnh trong C#

Tôi chưa thực hiện bất kỳ điều tra kỹ lưỡng nào nhưng có vẻ như một số cuộc gọi được thực hiện từ bên trong hàm dựng tĩnh không hoàn thành vì một số lý do.

Vì vậy, tôi muốn biết nơi có bất kỳ cạm bẫy nào khi sử dụng các hàm tạo tĩnh trong C#? Cụ thể hơn, có bất kỳ điều gì cần tránh bằng mọi giá và không được sử dụng từ bên trong hàm dựng tĩnh không?

Trả lời

25

Có một số cạm bẫy đối với các nhà xây dựng tĩnh. Ví dụ: nếu một hàm dựng tĩnh throws an exception, bạn sẽ tiếp tục nhận được TypeInitializationException bất cứ khi nào bạn truy cập bất kỳ thành viên nào của nó.

Nếu một nhà xây dựng tĩnh ném một ngoại lệ, thời gian chạy sẽ không gọi nó lần thứ hai và loại sẽ vẫn chưa được khởi tạo trong suốt thời gian của miền ứng dụng mà chương trình của bạn đang chạy.

Nói chung, các lớp tĩnh chỉ nên được sử dụng trong các tình huống không trạng thái mà bạn sẽ không cần bất kỳ khởi tạo nào. Nếu lớp học của bạn cần phải được khởi tạo, bạn có thể được tốt hơn bằng cách sử dụng singleton pattern, có thể lazily initialized về tiếp cận đầu tiên:

public class MyClass 
{ 
    private static readonly Lazy<MyClass> current = 
     new Lazy<MyClass>(() => new MyClass()); 

    public static MyClass Current 
    { 
     get { return current.Value; } 
    } 

    private MyClass() 
    { 
     // Initialization goes here. 
    } 

    public void Foo() 
    { 
     // ... 
    } 

    public void Bar() 
    { 
     // ... 
    } 
} 

static void Main(string[] args) 
{ 
    MyClass.Current.Foo(); // Initialization only performed here. 
    MyClass.Current.Bar(); 
    MyClass.Current.Foo(); 
} 

Sửa: Tôi đã làm một số đọc thêm lên về vấn đề này, và dường như static constructors do gây deadlocks nếu bạn thực hiện các hoạt động chặn (ví dụ như không đồng bộ gọi lại hoặc đồng bộ hóa thread) bên trong chúng.

CLR sử dụng nội bộ khóa để ngăn khởi tạo kiểu (các hàm tạo tĩnh) được thực hiện nhiều lần đồng thời. Vì vậy, nếu constructor tĩnh của bạn cố gắng truy cập vào một thành viên khác của kiểu khai báo của nó từ một luồng khác, nó chắc chắn sẽ bế tắc. Vì “một thành viên khác” có thể là một hàm ẩn danh được khai báo như là một phần của một hoạt động PLINQ hoặc TPL, các lỗi này có thể tinh tế và khó xác định.

Igor Ostrovsky (MSFT) giải thích điều này trong bài viết Static constructor deadlocks của mình, cung cấp các ví dụ sau đây của một bế tắc:

using System.Threading; 

class MyClass 
{ 
    static void Main() { /* Won’t run... the static constructor deadlocks */ } 

    static MyClass() 
    { 
     Thread thread = new Thread(arg => { }); 
     thread.Start(); 
     thread.Join(); 
    } 
} 

Trong ví dụ trên, các chủ đề mới cần phải truy cập vào các chức năng ẩn danh rỗng, { }, được xác định như gọi lại của nó. Tuy nhiên, vì chức năng ẩn danh được biên dịch như một phương pháp riêng khác của MyClass đằng sau hậu trường, chuỗi mới không thể truy cập nó trước khi loại bắt đầu loại MyClass. Và, vì hàm xây dựng tĩnh MyClass cần phải đợi luồng mới hoàn thành trước (vì thread.Join()), một bế tắc xảy ra sau đó.

+0

Có điều gì khác ngoài ngoại lệ được ném bên trong hàm tạo không? Ví dụ, những gì có thể giải thích "bế tắc" như kịch bản tôi đang gặp phải? Có khóa nào liên quan đến các loại tĩnh bằng cách nào đó đằng sau hậu trường không? –

+0

@liortal: Đã trả lời ở trên. – Douglas

+0

Có sự khác biệt nào giữa những gì bạn có trong ví dụ đầu tiên và các dòng này không? 'riêng tĩnh chỉ đọc Lazy hiện tại; tĩnh MyClass {current = new Lazy (() => new MyClass()); } ' (xin lỗi dường như không thể định dạng đúng :)) – Mark

3

Có, có một số cạm bẫy, phần lớn liên quan đến thời điểm lớp học được khởi tạo. Về cơ bản, một lớp với một hàm tạo tĩnh sẽ không được đánh dấu bằng cờ beforefieldinit, cho phép thời gian chạy khởi tạo nó sau này.

Hãy xem this article để biết thêm chi tiết.

0

Đây không phải là câu trả lời cho câu hỏi, nhưng quá dài để nhận xét, vì vậy tôi cung cấp nhận xét ở đây ...

Vì tôi không biết cho static class xây dựng, tôi đã sử dụng sau chương trình (giản thể) để cung cấp cho tôi với độc thân:

public class SomeSingleton { 
    static _instance; 
    static public SomeSingleton Instance { 
     get { 
      if (_instance==null) { 
       _instance=new SomeSingleton(); 
      } 
      return _instance; 
     } 
    } 
} 

Sau đó, bạn sử dụng

SomeSingleton.Instance.MyProp = 3; 

Và lần đầu tiên sử dụng các thành viên Instance sẽ xây dựng singleton của bạn.

Tôi đoán rằng nó là OK kể từ khi instantiation của singletons nếu có nhiều lớp như vậy được thực hiện theo thứ tự thích hợp.

+0

Nó không trả lời câu hỏi ... và một lớp tĩnh là * không * giống như một Singleton (ví dụ bạn không thể chuyển một lớp tĩnh như một tham số, mà bạn có thể làm với một singleton) –

+4

Khởi tạo của bạn là không an toàn chỉ. Nếu thuộc tính 'Instance' được truy cập đồng thời bởi nhiều luồng, chúng có thể nhận được các cá thể khác nhau của“ singleton ”. Điều này có thể hoặc không có thể là một vấn đề trong trường hợp cụ thể, nhưng nó phá vỡ mô hình đơn nói chung. Nếu bạn đang sử dụng .NET 4 (hoặc mới hơn), bạn nên chuyển sang 'Lazy '; nếu không, bạn nên cân nhắc sử dụng 'lock' để đồng bộ hóa khởi tạo. – Douglas

+0

@Douglas cảm ơn. Tôi sử dụng khóa, vì tôi đang ở trong .net 2.0 :) –

Các vấn đề liên quan