
Singleton を C# で
Singleton は、 生成に関するデザインパターンの一つで、 この種類のオブジェクトがただ一つだけ存在することを保証し、 他のコードに対して唯一のアクセス・ポイントを提供します。
Singleton には、 大域変数とほぼ同じ長所と短所があります。 両方とも随分と便利ですが、 コードのモジュール性を犠牲にしています。
シングルトンのクラスに依存しているあるクラスを使う場合、 シングルトンのクラスも一緒に使う必要があります。 ほとんどの場合、 この制限は、 ユニット・テストの作成で問題となります。
複雑度:
人気度:
使用例: 多くの開発者は、 Singleton をアンチ・パターンと見なしています。 C# コードでの使用が減少したのはこのためです。
見つけ方: Singleton は、 キャッシュされた同一オブジェクトを返す静的生成メソッドで識別できます。
素朴なシングルトン
いい加減なシングルトンの実装は、 超簡単です。 コンストラクターを隠して、 静的生成メソッドを一つ書くだけです。
同じクラスは、 マルチ・スレッド環境下では正しく動作しません。 複数のスレッドが、 生成メソッドを同時に呼ぶことができ、 シングルトン・クラスの複数のインスタンスができてしまいます。
Program.cs: 概念的な例
using System; namespace RefactoringGuru.DesignPatterns.Singleton.Conceptual.NonThreadSafe { // The Singleton class defines the `GetInstance` method that serves as an // alternative to constructor and lets clients access the same instance of // this class over and over. // EN : The Singleton should always be a 'sealed' class to prevent class // inheritance through external classes and also through nested classes. public sealed class Singleton { // The Singleton's constructor should always be private to prevent // direct construction calls with the `new` operator. private Singleton() { } // The Singleton's instance is stored in a static field. There there are // multiple ways to initialize this field, all of them have various pros // and cons. In this example we'll show the simplest of these ways, // which, however, doesn't work really well in multithreaded program. private static Singleton _instance; // This is the static method that controls the access to the singleton // instance. On the first run, it creates a singleton object and places // it into the static field. On subsequent runs, it returns the client // existing object stored in the static field. public static Singleton GetInstance() { if (_instance == null) { _instance = new Singleton(); } return _instance; } // Finally, any singleton should define some business logic, which can // be executed on its instance. public void someBusinessLogic() { // ... } } class Program { static void Main(string[] args) { // The client code. Singleton s1 = Singleton.GetInstance(); Singleton s2 = Singleton.GetInstance(); if (s1 == s2) { Console.WriteLine("Singleton works, both variables contain the same instance."); } else { Console.WriteLine("Singleton failed, variables contain different instances."); } } } }
Output.txt: 実行結果
Singleton works, both variables contain the same instance.
スレッド・セーフなシングルトン
問題を可決するには、 最初のシングルトン・オブジェクトの生成の間、 スレッドを同期する必要があります。
Program.cs: 概念的な例
using System; using System.Threading; namespace Singleton { // This Singleton implementation is called "double check lock". It is safe // in multithreaded environment and provides lazy initialization for the // Singleton object. class Singleton { private Singleton() { } private static Singleton _instance; // We now have a lock object that will be used to synchronize threads // during first access to the Singleton. private static readonly object _lock = new object(); public static Singleton GetInstance(string value) { // This conditional is needed to prevent threads stumbling over the // lock once the instance is ready. if (_instance == null) { // Now, imagine that the program has just been launched. Since // there's no Singleton instance yet, multiple threads can // simultaneously pass the previous conditional and reach this // point almost at the same time. The first of them will acquire // lock and will proceed further, while the rest will wait here. lock (_lock) { // The first thread to acquire the lock, reaches this // conditional, goes inside and creates the Singleton // instance. Once it leaves the lock block, a thread that // might have been waiting for the lock release may then // enter this section. But since the Singleton field is // already initialized, the thread won't create a new // object. if (_instance == null) { _instance = new Singleton(); _instance.Value = value; } } } return _instance; } // We'll use this property to prove that our Singleton really works. public string Value { get; set; } } class Program { static void Main(string[] args) { // The client code. Console.WriteLine( "{0}\n{1}\n\n{2}\n", "If you see the same value, then singleton was reused (yay!)", "If you see different values, then 2 singletons were created (booo!!)", "RESULT:" ); Thread process1 = new Thread(() => { TestSingleton("FOO"); }); Thread process2 = new Thread(() => { TestSingleton("BAR"); }); process1.Start(); process2.Start(); process1.Join(); process2.Join(); } public static void TestSingleton(string value) { Singleton singleton = Singleton.GetInstance(value); Console.WriteLine(singleton.Value); } } }
Output.txt: 実行結果
FOO FOO
もっと知りたいですか?
C# には、 もっと特殊な Singleton パターンの変わり種があります。 詳しくは、 下記の記事をご覧ください:
C# in Depth: Implementing Singleton (ディープ C#: シングルトンの実装)