Một singleton trong C # là gì?


182

Singleton là gì và khi nào tôi nên sử dụng nó?


2
Bản sao có thể có: stackoverflow.com/questions/246710/
Khắc

4
Ngoài ra, Singleton là một trong những mẫu thiết kế bị lạm dụng và sử dụng rộng rãi nhất trong lập trình OO.
ChaosPandion

3
@Fabiano: Bởi vì nó có cách tạo ra các khớp nối không có ý nghĩa (làm thế nào tôi có Xthể nói chuyện Y? Chỉ cần tạo Ymột singleton!), Điều này dẫn đến khó khăn trong việc kiểm tra / gỡ lỗi và phong cách lập trình theo thủ tục. Đôi khi Singletons là cần thiết; hầu hết thời gian, không.
Aaronaught

3
Đây là một trong những câu hỏi phỏng vấn điện thoại tiêu chuẩn của tôi. Câu trả lời đúng là: không bao giờ.
jonnii

3
@jonnii thật tốt, nó giúp cảnh báo các nhà phát triển tương lai xem ông chủ đó là người như thế nào!
Cậu bé

Câu trả lời:


145

Một singleton là một lớp chỉ cho phép tạo một thể hiện của chính nó - và cho phép truy cập đơn giản, dễ dàng vào thể hiện đã nói. Tiền đề singleton là một mô hình phát triển phần mềm.

Có một triển khai C # "Thực hiện mẫu Singleton trong C #" bao gồm hầu hết những gì bạn cần biết - bao gồm một số lời khuyên tốt về an toàn luồng .

Thành thật mà nói, rất hiếm khi bạn cần thực hiện một singleton - theo tôi đó là một trong những điều bạn nên chú ý, ngay cả khi nó không được sử dụng quá thường xuyên.


2
hướng dẫn tốt đẹp nhưng tào lao thần thánh họ đã làm gì để thụt mã
Inspi

Đây là một liên kết trực tiếp hơn với những gì tôi coi là triển khai lý tưởng vào năm 2020. Đó là, " sử dụng loại <T> Lazy của .NET 4 ", cũng như liên kết đến Microsoft Doc cho Lazy<T> Class.
Chiramisu

52

Bạn đã yêu cầu C #. Ví dụ tầm thường:


public class Singleton
{
    private Singleton()
    {
        // Prevent outside instantiation
    }

    private static readonly Singleton _singleton = new Singleton();

    public static Singleton GetSingleton()
    {
        return _singleton;
    }
}

14
không chủ đề safe.two có thể gọi cùng một lúc và có thể tạo hai đối tượng riêng biệt.
Alagesan Palani

5
@Alagesan Palani, quả thực bạn đúng. Tôi không thành thạo các chi tiết cấp thấp của khởi tạo cấp độ lớp, nhưng tôi nghĩ rằng sự thay đổi tôi đã thực hiện giải quyết mối quan tâm an toàn luồng.
Chris Simmons

3
chắc chắn, tôi KHÔNG chỉ bạn là sai. tôi đang đưa ra một gợi ý cho người đọc về an toàn luồng, để họ sẽ cẩn thận nếu họ phải đối phó với nó.
Alagesan Palani

9
Không, tôi nghĩ nhận xét của bạn là quan trọng. Cho rằng một cá thể được cho là sẽ giao một - và chỉ một cá thể, điều kiện cuộc đua ở đây mở ra khả năng có nhiều hơn một con được giao. Xem phiên bản bây giờ, với khởi tạo trường tĩnh. Tôi tin rằng điều này khắc phục vấn đề an toàn luồng, nếu tôi đọc tài liệuSO này trả lời đúng.
Chris Simmons

1
@AlagesanPalani, tôi thấy rằng bạn đã nói rằng một số câu trả lời khác không phải là chủ đề an toàn. Bạn có quan tâm để cung cấp một giải pháp an toàn cho chủ đề?
Bonez024

39

Nó là gì: Một lớp chỉ có một thể hiện liên tục trong suốt vòng đời của một ứng dụng. Xem mẫu Singleton .

Khi bạn nên sử dụng nó: Càng ít càng tốt. Chỉ khi bạn hoàn toàn chắc chắn rằng bạn cần nó. Tôi miễn cưỡng nói "không bao giờ", nhưng thường có một sự thay thế tốt hơn, chẳng hạn như Dependency Injection hoặc đơn giản là một lớp tĩnh.


16
Tôi không chắc chắn rằng một lớp tĩnh là một sự thay thế tốt hơn so với một đơn ... nó thực sự phụ thuộc vào tình huống và ngôn ngữ.
marcgg

5
Các lớp tĩnh không hành xử giống như một singleton, một singleton có thể được truyền vào các phương thức như một tham số trong khi một lớp tĩnh thì không thể.
TabbyCool

4
Đồng ý với marcgg - Tôi không thấy một lớp tĩnh là một thay thế tốt cho singletons, bởi vì bạn vẫn có vấn đề về việc cung cấp một chất thay thế, ví dụ như trong quá trình thử nghiệm một thành phần phụ thuộc vào lớp này. Nhưng tôi cũng thấy các cách sử dụng khác nhau, một lớp tĩnh thường được sử dụng cho các hàm tiện ích độc lập độc lập với trạng thái, trong đó một singleton là một thể hiện của lớp thực tế và nó thường sẽ lưu trữ một trạng thái. Tôi hoàn toàn đồng ý sử dụng DI thay thế, và sau đó nói với bộ chứa DI của bạn rằng bạn muốn nó chỉ sử dụng một thể hiện duy nhất của lớp đó.
Pete

9
Tôi đánh giá thấp câu trả lời này vì nó không cho tôi biết thông tin về thời điểm sử dụng nó. "Chỉ khi bạn cần" thực sự không cung cấp cho tôi bất kỳ thông tin nào cho người mới biết đến người độc thân.
Sergio Tapia

9
@Adkins: DI là viết tắt của Dependency Injection, nghĩa là khi bất kỳ phụ thuộc lớp nào được truyền qua (thường là) một hàm tạo hoặc thuộc tính công cộng. Một mình DI không giải quyết được vấn đề "khoảng cách", nhưng nó thường được triển khai cùng với bộ chứa Inversion-of-Control (IoC) biết cách tự động khởi tạo bất kỳ phụ thuộc nào. Vì vậy, nếu bạn đang tạo một Singleton để giải quyết vấn đề "X không biết cách tìm / nói chuyện với Y", một sự kết hợp giữa DI và IoC có thể giải quyết cùng một vấn đề với khớp nối lỏng lẻo hơn.
Aaronaught

27

một cách khác để triển khai singleton trong c #, cá nhân tôi thích cách này vì bạn có thể truy cập thể hiện của lớp singeton như một thuộc tính thay vì một phương thức.

public class Singleton
    {
        private static Singleton instance;

        private Singleton() { }

        public static Singleton Instance
        {
            get
            {
                if (instance == null)
                    instance = new Singleton();
                return instance;
            }
        }

        //instance methods
    }

nhưng tốt, theo như tôi biết cả hai cách đều được coi là "đúng", vì vậy đó chỉ là một thứ thuộc về hương vị cá nhân.


11
không chủ đề safe.two có thể gọi cùng một lúc và có thể tạo hai đối tượng riêng biệt.
Alagesan Palani

11
using System;
using System.Collections.Generic;
class MainApp
{
    static void Main()
    {
        LoadBalancer oldbalancer = null;
        for (int i = 0; i < 15; i++)
        {
            LoadBalancer balancerNew = LoadBalancer.GetLoadBalancer();

            if (oldbalancer == balancerNew && oldbalancer != null)
            {
                Console.WriteLine("{0} SameInstance {1}", oldbalancer.Server, balancerNew.Server);
            }
            oldbalancer = balancerNew;
        }
        Console.ReadKey();
    }
}

class LoadBalancer
{
    private static LoadBalancer _instance;
    private List<string> _servers = new List<string>();
    private Random _random = new Random();

    private static object syncLock = new object();

    private LoadBalancer()
    {
        _servers.Add("ServerI");
        _servers.Add("ServerII");
        _servers.Add("ServerIII");
        _servers.Add("ServerIV");
        _servers.Add("ServerV");
    }

    public static LoadBalancer GetLoadBalancer()
    {
        if (_instance == null)
        {
            lock (syncLock)
            {
                if (_instance == null)
                {
                    _instance = new LoadBalancer();
                }
            }
        }

        return _instance;
    }

    public string Server
    {
        get
        {
            int r = _random.Next(_servers.Count);
            return _servers[r].ToString();
        }
    }
}

Tôi đã lấy mã từ dofactory.com , không có gì lạ mắt nhưng tôi thấy điều này tốt hơn nhiều so với các ví dụ với cuốn sách bổ sung của Foo và Bar từ Judith Bishop trên Mẫu thiết kế C # 3.0 có ví dụ về ứng dụng đang hoạt động trong mac dock.

Nếu bạn nhìn vào mã chúng tôi đang thực sự xây dựng các đối tượng mới vào cho vòng lặp, do đó tạo ra đối tượng mới nhưng reuses dụ như một kết quả trong đó oldbalancer và newbalancer có cùng một ví dụ, như thế nào? do từ khóa tĩnh được sử dụng trên hàm GetLoadBalancer () , mặc dù có giá trị máy chủ khác nhau là danh sách ngẫu nhiên, tĩnh trên GetLoadBalancer () thuộc về chính loại đó chứ không phải là một đối tượng cụ thể.

Ngoài ra có khóa kiểm tra kép ở đây

if (_instance == null)
            {
                lock (syncLock)
                {
                    if (_instance == null)

kể từ MSDN

Từ khóa khóa đảm bảo rằng một luồng không nhập một phần quan trọng của mã trong khi một luồng khác nằm trong phần quan trọng. Nếu một luồng khác cố gắng nhập mã bị khóa, nó sẽ chờ, chặn, cho đến khi đối tượng được giải phóng.

do đó, khóa loại trừ lẫn nhau mọi lúc được phát ra, ngay cả khi nó không cần thiết để chúng tôi kiểm tra null.

Hy vọng nó sẽ giúp làm rõ hơn.

Và xin vui lòng bình luận nếu tôi hiểu là chỉ đạo sai cách.


6

Một Singleton (và điều này không gắn với C #, đó là mẫu thiết kế OO) là khi bạn muốn chỉ cho phép MỘT thể hiện của một lớp trong toàn bộ ứng dụng của bạn. Các khoản sử dụng thường bao gồm các tài nguyên toàn cầu, mặc dù tôi sẽ nói từ kinh nghiệm cá nhân, chúng thường là nguồn gốc của nỗi đau lớn.


5

Trong khi chỉ có thể có một thể hiện của một singleton, nó không giống như một lớp tĩnh. Một lớp tĩnh chỉ có thể chứa các phương thức tĩnh và không bao giờ có thể được khởi tạo, trong khi đó thể hiện của một singleton có thể được sử dụng theo cùng một cách như bất kỳ đối tượng nào khác.


2

Đó là một mẫu thiết kế và nó không dành riêng cho c #. Thông tin thêm về nó trên internet và SO, như trên bài viết trên wikipedia này .

Trong công nghệ phần mềm, mẫu singleton là một mẫu thiết kế được sử dụng để hạn chế khởi tạo một lớp cho một đối tượng. Điều này hữu ích khi chính xác một đối tượng là cần thiết để phối hợp các hành động trên toàn hệ thống. Khái niệm này đôi khi được khái quát hóa cho các hệ thống hoạt động hiệu quả hơn khi chỉ có một đối tượng tồn tại hoặc hạn chế việc khởi tạo đối với một số đối tượng nhất định (giả sử, năm). Một số người coi đó là một mô hình chống đối, đánh giá rằng nó bị lạm dụng quá mức, đưa ra những hạn chế không cần thiết trong các tình huống mà một trường hợp duy nhất của một lớp không thực sự cần thiết và đưa trạng thái toàn cầu vào một ứng dụng.

Bạn nên sử dụng nó nếu bạn muốn một lớp chỉ có thể được cung cấp một lần.


2

Tôi sử dụng nó cho dữ liệu tra cứu. Tải một lần từ DB.

public sealed class APILookup
    {
        private static readonly APILookup _instance = new APILookup();
        private Dictionary<string, int> _lookup;

        private APILookup()
        {
            try
            {
                _lookup = Utility.GetLookup();
            }
            catch { }
        }

        static APILookup()
        {            
        }

        public static APILookup Instance
        {
            get
            {
                return _instance;
            }
        }
        public Dictionary<string, int> GetLookup()
        {
            return _lookup;
        }

    }

2

Singleton là gì:
Đây là một lớp chỉ cho phép tạo một thể hiện của chính nó và thường cung cấp quyền truy cập đơn giản vào thể hiện đó.

Khi nào bạn nên sử dụng:
Nó phụ thuộc vào tình huống.

Lưu ý: vui lòng không sử dụng trên kết nối db, để có câu trả lời chi tiết, vui lòng tham khảo câu trả lời của @Chad Grant

Đây là một ví dụ đơn giản về một Singleton:

public sealed class Singleton
{
    private static readonly Singleton instance = new Singleton();

    // Explicit static constructor to tell C# compiler
    // not to mark type as beforefieldinit
    static Singleton()
    {
    }

    private Singleton()
    {
    }

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

Bạn cũng có thể sử dụng Lazy<T>để tạo của bạn Singleton.

Xem ở đây để biết ví dụ chi tiết hơn bằng cách sử dụngLazy<T>


1

Đây là singleton là gì: http://en.wikipedia.org/wiki/Singleton_potype

Tôi không biết C #, nhưng thực tế nó giống nhau ở tất cả các ngôn ngữ, chỉ khác nhau khi thực hiện.

Nói chung, bạn nên tránh singleton khi có thể, nhưng trong một số trường hợp, điều đó rất thuận tiện.

Xin lỗi vì tiếng Anh của tôi ;)


Tiếng Anh của bạn vẫn ổn :)
FrenkyB

1

Lớp Singleton được sử dụng để tạo một thể hiện duy nhất cho toàn bộ miền ứng dụng.

public class Singleton
{
    private static Singleton singletonInstance = CreateSingleton();

    private Singleton()
    {
    }

    private static Singleton CreateSingleton()
    {
        if (singletonInstance == null)
        {
            singletonInstance = new Singleton();
        }

        return singletonInstance;
    }

    public static Singleton Instance
    {
        get { return singletonInstance; }            
    }
}

Trong bài viết này, nó được mô tả làm thế nào chúng ta có thể tạo lớp singleton an toàn luồng bằng cách sử dụng biến chỉ đọc và sử dụng thực tế của chúng trong các ứng dụng.


1

Tôi biết đã rất muộn để trả lời câu hỏi nhưng với Auto-Property bạn có thể làm điều gì đó tương tự:

public static Singleton Instance { get; } = new Singleton();

SingletonLớp học của bạn ở đâu và có thể thông qua, trong trường hợp này là thuộc tính chỉ đọc Instance.


0

EX Bạn có thể sử dụng Singleton cho thông tin toàn cầu cần được tiêm.

Trong trường hợp của tôi, tôi đã giữ chi tiết người dùng được ghi nhật ký (tên người dùng, quyền, v.v.) trong Lớp tĩnh toàn cầu. Và khi tôi cố gắng thực hiện Kiểm thử đơn vị, không có cách nào tôi có thể đưa sự phụ thuộc vào các lớp Trình điều khiển. Vì vậy, tôi đã thay đổi mẫu Tĩnh của tôi thành mẫu Singleton.

public class SysManager
{
    private static readonly SysManager_instance = new SysManager();

    static SysManager() {}

    private SysManager(){}

    public static SysManager Instance
    {
        get {return _instance;}
    }
}

http://csharpindepth.com/Articles/General/Singleton.aspx#cctor


0

Chúng ta cần sử dụng Mẫu thiết kế Singleton trong C # khi chúng ta cần đảm bảo rằng chỉ một phiên bản của một lớp cụ thể sẽ được tạo và sau đó cung cấp quyền truy cập toàn cầu đơn giản vào phiên bản đó cho toàn bộ ứng dụng.

Các kịch bản thời gian thực nơi bạn có thể sử dụng Mẫu thiết kế Singleton: Proxy dịch vụ: Như chúng ta biết, việc gọi API dịch vụ là một hoạt động mở rộng trong một ứng dụng. Quá trình chiếm phần lớn thời gian là tạo ứng dụng khách Dịch vụ để gọi API dịch vụ. Nếu bạn tạo proxy Dịch vụ dưới dạng Singleton thì nó sẽ cải thiện hiệu suất của ứng dụng của bạn.

Mặt tiền: Bạn cũng có thể tạo các kết nối Cơ sở dữ liệu dưới dạng Singleton có thể cải thiện hiệu suất của ứng dụng.

Nhật ký: Trong một ứng dụng, thực hiện thao tác I / O trên một tệp là một thao tác đắt tiền. Nếu bạn tạo cho bạn Logger dưới dạng Singleton thì nó sẽ cải thiện hiệu suất của hoạt động I / O.

Chia sẻ dữ liệu: Nếu bạn có bất kỳ giá trị không đổi hoặc giá trị cấu hình nào thì bạn có thể giữ các giá trị này trong Singleton để các thành phần khác của ứng dụng có thể được đọc.

Bộ nhớ đệm: Như chúng ta biết, việc tìm nạp dữ liệu từ cơ sở dữ liệu là một quá trình tốn thời gian. Trong ứng dụng của bạn, bạn có thể lưu trữ bộ đệm chính và cấu hình trong bộ nhớ để tránh các cuộc gọi DB. Trong các tình huống như vậy, lớp Singleton có thể được sử dụng để xử lý bộ đệm ẩn với đồng bộ hóa luồng theo cách hiệu quả giúp cải thiện đáng kể hiệu năng của ứng dụng.

Nhược điểm của Mẫu thiết kế Singleton trong C # Những nhược điểm của việc sử dụng Mẫu thiết kế Singleton trong C # như sau:

Kiểm thử đơn vị là rất khó vì nó đưa một trạng thái toàn cầu vào một ứng dụng. Nó làm giảm khả năng song song trong một chương trình vì để truy cập thể hiện singleton trong môi trường đa luồng, bạn cần tuần tự hóa đối tượng bằng cách sử dụng khóa.

Tôi đã lấy điều này từ bài viết sau.

https://dotnettutorials.net/lesson/singleton-design-potype/


0

Chủ đề Singleton an toàn mà không sử dụng ổ khóa và không có ngay lập tức lười biếng.

Việc triển khai này có một hàm tạo tĩnh, do đó, nó chỉ thực hiện một lần cho mỗi Miền ứng dụng.

public sealed class Singleton
{

    static Singleton(){}

    private Singleton(){}

    public static Singleton Instance { get; } = new Singleton();

}
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.