单例模式(Singleton Pattern)
引用自:http://csharpindepth.com/Articles/General/Singleton.aspx?printable=true
public sealed class Singleton { private Singleton() { } public static Singleton Instance { get { return Nested.instance; } } private class Nested { //Explicit static constructor to tell C# compiler not to mark type as beforefieldinit static Nested() { } internal static readonly Singleton instance = new Singleton(); } }
Here, instantiation is triggered by the first reference to the static member of the nested class, which only occurs in Instance. This means the implementation is fully lazy, but has all the performance benefits of the previous ones. Note that although nested classes have access to the enclosing class's private members, the reverse is not true, hence the need for instance to be internal here. That doesn't raise any other problems, though, as the class itself is private. The code is a bit more complicated in order to make the instantiation lazy, however.
Sixth version - using .NET 4's Lazy<T> type
If you're using .NET 4 (or higher), you can use the System.Lazy<T> type to make the laziness really simple. All you need to do is pass a delegate to the constructor which calls the Singleton constructor - which is done most easily with a lambda expression.
public sealed class Singleton { private static readonly Lazy<Singleton> lazy = new Lazy<Singleton>(() => new Singleton()); public static Singleton Instance { get { return lazy.Value; } } private Singleton() { } }
It's simple and performs well. It also allows you to check whether or not the instance has been created yet with the IsValueCreated property, if you need that.
In many cases, you won't actually require full laziness - unless your class initialization does something particularly time-consuming(耗时的;旷日持久的), or has some side-effect(副作用) elsewhere(在别处;到别处), it's probably fine to leave out(遗漏,省去,不考虑) the explicit static constructor shown above. This can increase performance as it allows the JIT compiler to make a single check (for instance at the start of a method) to ensure that the type has been initialized, and then assume it from then on. If your singleton instance is referenced within a relatively tight loop(紧凑循环), this can make a (relatively) significant performance difference. You should decide whether or not fully lazy instantiation is required, and document this decision appropriately within the class.
A lot of the reason for this page's existence is people trying to be clever(聪明的,机灵的,熟练的), and thus coming up with the double-checked locking algorithm. There is an attitude of locking being expensive which is common and misguided(误导). I've written a very quick benchmark which just acquires singleton instances in a loop a billion ways, trying different variants. It's not terribly scientific, because in real life you may want to know how fast it is if each iteration actually involved a call into a method fetching the singleton, etc. However, it does show an important point. On my laptop, the slowest solution (by a factor of about 5) is the locking one (solution 2). Is that important? Probably not, when you bear in mind that it still managed to acquire the singleton a billion times in under 40 seconds. (Note: this article was originally written quite a while ago now - I'd expect better performance now.) That means that if you're "only" acquiring the singleton four hundred thousand times per second, the cost of the acquisition(获得物,获得,收购) is going to be 1% of the performance - so improving it isn't going to do a lot. Now, if you are acquiring the singleton that often - isn't it likely you're using it within a loop? If you care that much about improving the performance a little bit, why not declare a local variable outside the loop, acquire the singleton once and then loop. Bingo, even the slowest implementation becomes easily adequate(充足的,适当的,胜任的).
I would be very interested to see a real world application where the difference between using simple locking and using one of the faster solutions actually made a significant performance difference.
Exceptions
Sometimes, you need to do work in a singleton constructor which may throw an exception, but might not be fatal to the whole application. Potentially(可能地,潜在地), your application may be able to fix the problem and want to try again. Using type initializers to construct the singleton becomes problematic at this stage. Different runtimes handle this case differently, but I don't know of any which do the desired thing (running the type initializer again), and even if one did, your code would be broken on other runtimes. To avoid these problems, I'd suggest using the second pattern listed on the page - just use a simple lock, and go through the check each time, building the instance in the method/property if it hasn't already been successfully built.
Thanks to Andriy Tereshchenko for raising this issue.
Conclusion (modified slightly on January 7th 2006; updated Feb 12th 2011)
There are various different ways of implementing the singleton pattern in C#. A reader has written to me detailing a way he has encapsulated the synchronization(同步) aspect, which while I acknowledge(承认,答谢) may be useful in a few very particular situations (specifically where you want very high performance, and the ability to determine whether or not the singleton has been created, and full laziness regardless of other static members being called). I don't personally see that situation coming up often enough to merit(优点;优异;长处;值得) going further with on this page, but please mail me if you're in that situation.
My personal preference is for solution 4: the only time I would normally go away from it is if I needed to be able to call other static methods without triggering initialization, or if I needed to know whether or not the singleton has already been instantiated. I don't remember the last time I was in that situation, assuming I even have. In that case, I'd probably go for solution 2, which is still nice and easy to get right.
Solution 5 is elegant, but trickier than 2 or 4, and as I said above, the benefits it provides seem to only be rarely useful. Solution 6 is a simpler way to achieve laziness, if you're using .NET 4. It also has the advantage that it's obviously lazy. I currently tend to still use solution 4, simply through habit - but if I were working with inexperienced developers I'd quite possibly go for solution 6 to start with as an easy and universally applicable pattern.
(I wouldn't use solution 1 because it's broken, and I wouldn't use solution 3 because it has no benefits over 5.)

浙公网安备 33010602011771号