可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
I have a question, is this the correct approach to make a Generic Singleton?
public class Singleton<T> where T : class, new()
{
private static T instance = null;
private Singleton() { }
public static T Instancia
{
get
{
if (instance == null)
instance = new T();
return instance;
}
}
}
EDIT:
Checking some PDFs I found a generic Singleton made this other way, is this other correct?
public class Singleton<T> where T : class, new()
{
Singleton() { }
class SingletonCreator
{
static SingletonCreator() { }
// Private object instantiated with private constructor
internal static readonly T instance = new T();
}
public static T UniqueInstance
{
get { return SingletonCreator.instance; }
}
}
回答1:
The problem with a generic singleton factory is that since it is generic you do not control the "singleton" type that is instantiated so you can never guarantee that the instance you create will be the only instance in the application.
If a user can provide a type to as a generic type argument then they can also create instances of that type. In other words, you cannot create a generic singleton factory - it undermines the pattern itself.
回答2:
this is my point using .NET 4
public class Singleton<T> where T : class, new()
{
private Singleton (){}
private static readonly Lazy<T> instance = new Lazy<T>(()=> new T());
public static T Instance { get { return instance.Value; } }
}
Usage pattern:
var journalSingleton = Singleton<JournalClass>.Instance;
回答3:
Here is my implementation using a non-public constructor. The only issue right now is there is no way to have a custom constraint on C# generics, so I have to throw a run-time exception for derived classes with public default constructors instead of a compile-time error.
using System;
using System.Reflection;
using System.Threading;
/// <summary>
/// A generic abstract implementation of the Singleton design pattern (http://en.wikipedia.org/wiki/Singleton_pattern).
///
/// Derived type must contain a non-public default constructor to satisfy the rules of the Singleton Pattern.
/// If no matching constructor is found, an exception will be thrown at run-time. I am working on a StyleCop
/// constraint that will throw a compile-time error in the future.
///
/// Example Usage (C#):
///
/// class MySingleton : Singleton<MySingleton>
/// {
/// private const string HelloWorldMessage = "Hello World - from MySingleton";
///
/// public string HelloWorld { get; private set; }
///
/// // Note: *** Private Constructor ***
/// private MySingleton()
/// {
/// // Set default message here.
/// HelloWorld = HelloWorldMessage;
/// }
/// }
///
/// class Program
/// {
/// static void Main()
/// {
/// var mySingleton = MySingleton.Instance;
/// Console.WriteLine(mySingleton.HelloWorld);
/// Console.ReadKey();
/// }
/// }
/// </summary>
/// <typeparam name="T">Type of derived Singleton object (i.e. class MySingletone: Singleton<MySingleton>).</typeparam>
public abstract class Singleton<T> where T : class
{
/// <summary>
/// "_instance" is the meat of the Singleton<T> base-class, as it both holds the instance
/// pointer and the reflection based factory class used by Lazy<T> for instantiation.
///
/// Lazy<T>.ctor(Func<T> valueFactory,LazyThreadSafetyMode mode), valueFactory:
///
/// Due to the fact Lazy<T> cannot access a singleton's (non-public) default constructor and
/// there is no "non-public default constructor required" constraint available for C#
/// generic types, Lazy<T>'s valueFactory Lambda uses reflection to create the instance.
///
/// Lazy<T>.ctor(Func<T> valueFactory,LazyThreadSafetyMode mode), mode:
///
/// Explanation of selected mode (ExecutionAndPublication) is from MSDN.
///
/// Locks are used to ensure that only a single thread can initialize a Lazy<T> instance
/// in a thread-safe manner. If the initialization method (or the default constructor, if
/// there is no initialization method) uses locks internally, deadlocks can occur. If you
/// use a Lazy<T> constructor that specifies an initialization method (valueFactory parameter),
/// and if that initialization method throws an exception (or fails to handle an exception) the
/// first time you call the Lazy<T>.Value property, then the exception is cached and thrown
/// again on subsequent calls to the Lazy<T>.Value property. If you use a Lazy<T>
/// constructor that does not specify an initialization method, exceptions that are thrown by
/// the default constructor for T are not cached. In that case, a subsequent call to the
/// Lazy<T>.Value property might successfully initialize the Lazy<T> instance. If the
/// initialization method recursively accesses the Value property of the Lazy<T> instance,
/// an InvalidOperationException is thrown.
///
/// </summary>
private static readonly Lazy<T> _instance = new Lazy<T>(() =>
{
// Get non-public constructors for T.
var ctors = typeof (T).GetConstructors(BindingFlags.Instance | BindingFlags.NonPublic);
// If we can't find the right type of construcor, throw an exception.
if (!Array.Exists(ctors, (ci) => ci.GetParameters().Length == 0))
{
throw new ConstructorNotFoundException("Non-public ctor() note found.");
}
// Get reference to default non-public constructor.
var ctor = Array.Find(ctors, (ci) => ci.GetParameters().Length == 0);
// Invoke constructor and return resulting object.
return ctor.Invoke(new object[] {}) as T;
}, LazyThreadSafetyMode.ExecutionAndPublication);
/// <summary>
/// Singleton instance access property.
/// </summary>
public static T Instance
{
get { return _instance.Value; }
}
}
/// <summary>
/// Exception thrown by Singleton<T> when derived type does not contain a non-public default constructor.
/// </summary>
public class ConstructorNotFoundException : Exception
{
private const string ConstructorNotFoundMessage = "Singleton<T> derived types require a non-public default constructor.";
public ConstructorNotFoundException() : base(ConstructorNotFoundMessage) { }
public ConstructorNotFoundException(string auxMessage) : base(String.Format("{0} - {1}", ConstructorNotFoundMessage, auxMessage)) { }
public ConstructorNotFoundException(string auxMessage, Exception inner) : base(String.Format("{0} - {1}", ConstructorNotFoundMessage, auxMessage), inner) { }
}
回答4:
You can make a singleton base class using a bit of cheating (reflection).
You can (run-time) enforce a class has no public constructor.
public abstract class Singleton<T> where T : Singleton<T> {
private const string ErrorMessage = " must have a parameterless constructor and all constructors have to be NonPublic.";
private static T instance = null;
public static T Instance => instance ?? (instance = Create());
protected Singleton() {
//check for public constructors
var pconstr = typeof(T).GetConstructors(BindingFlags.Public | BindingFlags.Instance);
//tell programmer to fix his stuff
if (pconstr.Any())
throw new Exception(typeof(T) + ErrorMessage);
}
private static T Create() {
try {
//get nonpublic constructors
var constructors = typeof(T).GetConstructors(BindingFlags.NonPublic | BindingFlags.Instance);
//make sure there is but 1 and use that
return (T)constructors.Single().Invoke(null);
}
catch {
//tell programmer to fix his stuff
throw new Exception(typeof(T)+ErrorMessage);
}
}
}
回答5:
For a generic piece of code that will be reused, you should consider thread safety at the point where you create the singleton instance.
As it is, (instance == null)
could evaluate to true on separate threads.
回答6:
for creating generic Singleton factory you can use something like this class as your factory :
public abstract class BaseLazySingleton<T> where T : class
{
private static readonly Lazy<T> LazyInstance =
new Lazy<T>(CreateInstanceOfT, LazyThreadSafetyMode.ExecutionAndPublication);
#region Properties
public static T Instance
{
get { return LazyInstance.Value; }
}
#endregion
#region Methods
private static T CreateInstanceOfT()
{
return Activator.CreateInstance(typeof(T), true) as T;
}
protected BaseLazySingleton()
{
}
#endregion
}
Note that
- this generator is abstract so no one can create new Instance of this class.
- constructor method is protected Not public
回答7:
This how I did it, using the Current pattern (also thread safe initialization)
public static class Singleton<T>
{
private static readonly object Sync = new object();
public static T GetSingleton(ref T singletonMember, Func<T> initializer)
{
if (singletonMember == null)
{
lock (Sync)
{
if (singletonMember == null)
singletonMember = initializer();
}
}
return singletonMember;
}
}
Usage:
private static MyType _current;
public static MyType Current = Singleton<MyType>.GetSingleton(ref _current, () => new MyType());
Consume the singleton:
MyType.Current. ...
回答8:
It is possible without reflection.
We just need a generic class for the singleton pattern which takes two parameter - the implementation of the concrete singleton class and the interface for the concrete singleton.
The generic singleton class implements the singleton pattern and all the stuff you need - for example logging, locks or what else.
using System;
using System.Diagnostics;
namespace Singleton
{
class Program
{
static void Main(string[] args)
{
Something.Instance.SayHello();
}
}
/// <summary>
/// Generic singleton pattern implementation
/// </summary>
public class SingletonImplementation<Implementation, ImplementationInterface>
where Implementation : class, ImplementationInterface, new()
{
private SingletonImplementation() { }
private static Implementation instance = null;
public static ImplementationInterface Instance
{
get
{
// here you can add your singleton stuff, which you don't like to write all the time
if ( instance == null )
{
instance = new Implementation();
}
return instance;
}
}
}
/// <summary>
/// Interface for the concrete singleton
/// </summary>
public interface ISomething
{
void SayHello();
}
/// <summary>
/// Singleton "held" or "wrapper" which provides the instance of the concrete singleton
/// </summary>
public static class Something
{
// No need to define the ctor private, coz you can't do anything wrong or useful with an instance of Something
// private Implementation();
/// <summary>
/// Like common: the static instance to access the concrete singleton
/// </summary>
public static ISomething Instance => SingletonImplementation<ImplementationOfSomething, ISomething>.Instance;
/// <summary>
/// Concrete singleton implementation
/// </summary>
private class ImplementationOfSomething : ISomething
{
// No need to define the ctor private, coz the class is private.
// private Implementation();
public void SayHello()
{
Debug.WriteLine("Hello world.");
}
}
}
}