Singleton của Jon Skeet làm rõ


214
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 triển khai mẫu Singleton của Jon Skeet trong ứng dụng hiện tại của tôi trong C #.

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

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

    internal static readonly Singleton instance = new Singleton();

    Là một cái gì đó gọi là đóng cửa?

  2. Tôi không thể hiểu bình luận này

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

    Nhận xét này gợi ý gì cho chúng tôi?


12
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
John Antony Daniel Nolan

14
Hai điều được chấp nhận phổ biến: mặt trời mọc từ phía đông và Jon Skeet luôn luôn đúng. Nhưng tôi vẫn chưa chắc chắn về cái trước: P
akhil_mittal

2
@ thepirat000 - Nếu anh ấy chỉ là người tham gia SO / Meta, tôi có thể không đồng ý, nhưng anh ấy có đủ ảnh hưởng trong thế giới lập trình thực tế có thể thực sự hợp pháp - Tôi chắc chắn ai đó đã tạo ra nó ở điểm này hay điểm khác .

8
Phân loại câu hỏi này đang được thảo luận về meta .
BoltClock

Câu trả lời:


359
  1. Không, điều này 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 bên ngoài, bao gồm cả hàm tạo riêng ở đây.

  2. Đọc bài viết của tôi trên beforefieldinit . Bạn có thể hoặc không muốn nhà xây dựng tĩnh không hoạt động - nó phụ thuộc vào sự lười biếng đảm bảo bạn cần gì. Bạn nên lưu ý rằng .NET 4 thay đổi ngữ nghĩa khởi tạo kiểu thực tế một chút (vẫn nằm trong thông số kỹ thuật, nhưng lười hơn trước).

Bạn có thực sự cần mẫu này mặc dù? Bạn có chắc chắn không thể thoát khỏi:

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

    static Singleton() {}
    private Singleton() {}
}

12
@Anindya: Không, không sao đâu. Bạn có thể muốn gửi thư JetBrains để khiếu nại :)
Jon Skeet

2
@JonSkeet, tôi vừa nêu lên mối quan tâm với JetBrains về điều này (# RSRP-274373). Hãy xem những gì họ có thể đưa ra. :)
Anindya Chatterjee

3
@ Phim hoạt hình: Bạn không. Một người độc thân sống trong suốt thời gian của AppDomain.
Jon Skeet

2
@JonSkeet Có lý do nào để không sử dụng Lazy<T>để bạn không phải khai báo hàm tạo tĩnh cho BeforeFieldInithiệu ứng phụ ma thuật không?
Ed T

3
FieldBeforeInitMahaBharatatừMicrosoft
Amit Kumar Ghosh

49

Về câu hỏi (1): Câu trả lời từ Jon là chính xác, vì anh ta ngầm đánh dấu lớp 'Nested' riêng tư bằng cách không công khai 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 'riêng tư':

    private class Nested

Về câu hỏi (2): về cơ bản những gì bài viết về beforeinitfieldkhởi tạo kiểu cho bạn biết là nếu bạn không có hàm tạo 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 tạo tĩnh có thể khởi tạo các trường, điều đó 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 bộ thực thi khởi tạo các trường 'một cách 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 các singletons, bạn sẽ muốn nó khởi tạo càng lười càng tốt và không phải khi thời gian chạy nghĩ rằng nó sẽ khởi tạo biến của bạn - hoặc có lẽ bạn 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 đó đã đáp ứng bài đăng của Jon về 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 số 3 của anh ấy, được đánh dấu là 'sai', thực sự đúng (vì khóa tự động bao hàm một rào cản bộ nhớ khi thoát ). Nó cũng phải nhanh hơn singleton # 2 khi bạn sử dụng thể hiện nhiều hơn một lần (điều này ít nhiều là điểm của một singleton :-)). Vì vậy, nếu bạn thực sự cần một triển khai đơn lẻ lười biếng, có lẽ tôi sẽ thực hiện điều đó - vì những lý do đơn giản rằng (1) mọi người đều đọ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 các 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 bế tắc và hành vi bất ngờ với các ngoại lệ. Để biết chi tiết, xem: chế độ khóa của lazy , cụ thể là ExecutAndPublication.


62
Regarding question (1): The answer from Jon is correct ...Jon Skeet luôn luôn đúng ....
Noctis

72
Thêm điểm khi cố gắng trả lời về câu hỏi của Jon Skeet trong đó Jon Skeet đã trả lời.
valdetero

8
@valdetero Hahaha. Đây ... hahaha +1
akinuri
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.