среда, 1 декабря 2010 г.

“В жизни всегда есть место для подвига” © или как реализовать Singleton по-новому

Тема реализации шаблона Singleton так или иначе затрагивает жизнь любого разработчика – толи на собеседовании спросят (или самом спросить придется), толи в реальном проекте найдется применение (не приведи Господи). О том, как этого зверя вырастить, написано немало – и в MSDN, и в прочих интернетах (последняя статься считается канонической и ранее располагалась по другому адресу). В какой-то момент казалось (мне по крайней мере), что придумать что-нибудь новое в этой области невозможно. Ошибся.

Недавно на одном из проектов заказчик прислал кусок [псевдо]кода, в котором кроме всего прочего содержалась крайне любопытная реализация привычного шаблона (тот еще затейник – архитектура решения тоже заставляем “много думать”). В чистом виде (убирая конкретику проекта и применяя привычные для .NET схемы именования и форматирования) это выглядит так:





public interface ISingleton
{

}

class Singleton : ISingleton
{
public delegate ISingleton InstanceGetterDelegate();

private static Singleton _singleton;
private static InstanceGetterDelegate _instanceGetter;
static Singleton()
{
var type = System.Reflection.MethodBase.GetCurrentMethod().DeclaringType;
// assign the closure that executes on the first call of GetInstance() method
_instanceGetter = () =>
{
Singleton instance = null;
lock (type)
{
var rslt = Interlocked.CompareExchange<Singleton>(ref _singleton, null, null);
if (rslt == null) // indicates is first call
{
instance = new Singleton(/* init arguments go here*/);
Interlocked.CompareExchange<Singleton>(ref _singleton, null, instance);

// now replace with a closure that executes on all subsequent calls of GetInstance() method
InstanceGetterDelegate fn = () =>
{
return Interlocked.CompareExchange<Singleton>(ref _singleton, null, null);
};
Interlocked.Exchange<InstanceGetterDelegate>(ref _instanceGetter, fn);
}
else
{
instance = rslt; // was already set with singleton instance
}
}
return instance;
};
}
private Singleton() { } // disable default instance constructor
public static ISingleton GetInstance() { return _instanceGetter(); }
}


И вот тут, что называется, “накрывает”. Я бы назвал этот способ “JavaScript-style Singleton” (конечно же за использования замыканий). В целом (за исключение блокировки по Type (о чем я уже писал) и использования рефлексии в статическом конструкторе) реализация выглядит любопытной и уж во всяком случае расширяет горизонт.



HTH, AlexS