2010-03-31 23 views
201
public sealed class Singleton 
{ 
    Singleton() {} 

    public static Singleton Instance 
    { 
     get 
     { 
      return Nested.instance; 
     } 
    } 

    class Nested 
    { 
     // Explicit static constructor to tell C# compiler 
     // not to mark type as beforefieldinit 
     static Nested() {} 
     internal static readonly Singleton instance = new Singleton(); 
    } 
} 

Tôi muốn thực hiện Jon Skeet's Singleton pattern trong ứng dụng hiện tại của tôi trong C#.Singleton by Jon Skeet làm rõ

Tôi có hai nghi ngờ về mã

  1. Làm thế nào là nó có thể truy cập vào các lớp bên ngoài bên trong lớp lồng nhau? Ý tôi là

    internal static readonly Singleton instance = new Singleton(); 
    

    Thứ gì đó được gọi là đóng cửa?

  2. Tôi không thể hiểu nhận xét này

    // Explicit static constructor to tell C# compiler 
    // not to mark type as beforefieldinit 
    

    gì nhận xét này cho thấy chúng ta?

+11

haha ​​Tôi nghĩ rằng tôi đã nói rằng có một chút lo lắng lol ... hóa ra là một John Nolan khác nhau –

+66

Vì vậy, bằng cách đặt câu hỏi về mã của Jon Skeet, bạn sẽ nhận được 1000 lượt xem và phản hồi từ chính Skeet. –

+13

Hai điều được chấp nhận rộng rãi: mặt trời mọc từ phía đông và Jon Skeet luôn đúng. Nhưng tôi chưa chắc về điều này: P –

Trả lời

329
  1. Không, đây là không có gì để làm với đóng cửa. Một lớp lồng nhau có quyền truy cập vào các thành viên riêng của lớp ngoài của nó, bao gồm cả hàm tạo riêng tư ở đây.

  2. Đọc số article on beforefieldinit của tôi. Bạn có thể hoặc có thể không muốn các nhà xây dựng tĩnh no-op - nó phụ thuộc vào những gì laziness đảm bảo bạn cần. Bạn nên lưu ý rằng .NET 4 changes the actual type initialization semantics somewhat (vẫn nằm trong thông số kỹ thuật, nhưng lười hơn trước).

Bạn thực sự cần mẫu này mặc dù? Bạn có chắc chắn bạn không thể nhận được ngay với:

public sealed class Singleton 
{ 
    private static readonly Singleton instance = new Singleton(); 
    public static Singleton Instance { get { return instance; } } 

    static Singleton() {} 
    private Singleton() {} 
} 
+1

@JonSkeet - chỉ để cofirm; R # là phàn nàn về ctor tĩnh rỗng, do đó, nó gây ra bất kỳ vấn đề nếu tôi di chuyển khởi tạo biến dụ trong tĩnh ctor? –

+10

@Anindya: Không, được rồi. Bạn có thể muốn gửi JetBrains để khiếu nại mặc dù :) –

+2

@ JonSkeet, tôi chỉ nêu lên một mối quan ngại với JetBrains về điều này (# RSRP-274373). Hãy xem những gì họ có thể nghĩ ra. :) –

41

Về câu hỏi (1): Câu trả lời từ Jon là đúng, vì ông đã kiên quyết đánh dấu sự lớp 'lồng' tin bằng cách không làm cho nó công cộng hoặc nội bộ: -). Bạn cũng có thể làm điều đó một cách rõ ràng bằng cách thêm 'tin':

private class Nested 

Về câu hỏi (2): về cơ bản những gì the post about beforeinitfieldtype initialization nói với bạn là nếu bạn không có constructor tĩnh, thời gian chạy có thể khởi tạo nó bất cứ lúc nào (nhưng trước khi bạn sử dụng nó). Nếu bạn có một hàm tạo tĩnh, mã của bạn trong hàm dựng tĩnh có thể khởi tạo các trường, có nghĩa là thời gian chạy chỉ được phép khởi tạo trường khi bạn yêu cầu loại đó.

Vì vậy, nếu bạn không muốn thời gian chạy khởi tạo 'chủ động' trước khi bạn sử dụng chúng, hãy thêm một hàm tạo tĩnh. Dù bằng cách nào, nếu bạn đang triển khai thực đơn, bạn hoặc muốn nó khởi tạo là lười nhất có thể và không phải khi thời gian chạy nghĩ rằng nó nên khởi tạo biến của bạn - hoặc bạn có thể không quan tâm. Từ câu hỏi của bạn, tôi cho rằng bạn muốn họ càng muộn càng tốt.

Điều đó mang đến cho bài đăng của Jon về số singleton, là IMO chủ đề cơ bản của câu hỏi này. Oh và những nghi ngờ :-)

Tôi muốn chỉ ra rằng bài hát số 3 của anh ấy, mà anh ấy đánh dấu 'sai', thực sự là chính xác (vì khóa tự động implies a memory barrier on exit). Nó cũng nên nhanh hơn singleton # 2 khi bạn sử dụng cá thể nhiều hơn một lần (nhiều hay ít điểm của một singleton :-)).Vì vậy, nếu bạn thực sự cần triển khai thực đơn lười biếng, có lẽ tôi sẽ chọn cái đó - vì những lý do đơn giản là (1) rất rõ ràng cho mọi người đọc mã của bạn những gì đang diễn ra và (2) bạn biết điều gì sẽ xảy ra với ngoại lệ.

Trong trường hợp bạn đang tự hỏi: Tôi sẽ không bao giờ sử dụng singleton # 6 vì nó có thể dễ dàng dẫn đến deadlocks và hành vi bất ngờ với ngoại lệ. Để biết chi tiết, xem: lazy's locking mode, cụ thể là ExecutionAndPublication.

+51

'Về câu hỏi (1): Câu trả lời từ Jon là chính xác ...' Jon Skeet là ** luôn ** đúng ... – Noctis

+47

Điểm bổ sung để tìm câu trả lời về câu hỏi của Jon Skeet trong đó Jon Skeet đã trả lời . – valdetero

+4

@valdetero Hahaha. Cái này ... hahaha +1 – akinuri