V1.0 - 12/01/2004 - original
V1.1 - 31/01/2004 - forbedret formatering
V1.2 - 17/02/2004 - tilføj henvisning til MSDN artikel om lock typeof
V1.3 - 07/03/2004 - tilføj henvisning til VB.NET artikel
V1.4 - 10/05/2005 - ændre til at double locking heller ikke er sikker i .NET
Teori
Singleton pattern løser problemet med at man kun vil
have en enkelt instans af en given klasse.
Singleton pattern er en god objekt orienteret løsning på
samme problem som løses ikke objekt orienteret via en
klasse med kun static members og methods.
Singleton pattern er et såkaldt GoF pattern, hvilket
refererer til bogen "Design Patterns" af Erich Gamma, Richard Helm,
Ralph Johnson og John Vlissides (4 forfattere = Gang Of Four = GoF).
Kendetegnene ved en singleton klasse er:
- public static metode GetInstance eller property Instance
- private constructor
(den original GoF kode bruger protected constructor og det kan
man også godt, men min erfaring er at man ikke kan arve fra en
singleton klasse på fornuftig vis)
Eksempel
Her er et standard eksempel på en singleton klasse:
using System;
using System.Collections;
// singleton klasse
public class S1
{
// normale attributter eksemplificeret ved en ArrayList
private ArrayList list;
// den eneste instans der eksisterer
private static S1 instance = null;
// private constructor
private S1() {
list = new ArrayList();
}
// public static metode til at hente instance
public static S1 GetInstance()
{
if(instance == null)
{
instance = new S1();
}
return instance;
}
// normale metoder
public void Add(object o)
{
list.Add(o);
}
public ArrayList GetList()
{
return list;
}
}
eller:
using System;
using System.Collections;
// singleton klasse
public class S2
{
// normale attributter eksemplificeret ved en ArrayList
private ArrayList list;
// den eneste instans der eksisterer
private static S2 instance = new S2();
// private constructor
private S2() {
list = new ArrayList();
}
// public static metode til at hente instance
public static S2 GetInstance()
{
return instance;
}
// normale metoder
public void Add(object o)
{
list.Add(o);
}
public ArrayList GetList()
{
return list;
}
}
C# tillader imidlertid et elegant alternativ med brug af property:
using System;
using System.Collections;
public class S3
{
// normale attributter eksemplificeret ved en ArrayList
private ArrayList list;
// den eneste instans der eksisterer
private static S3 instance = null;
// private constructor
private S3() {
list = new ArrayList();
}
// property til at hente instance
public static S3 Instance
{
get {
if(instance == null)
{
instance = new S3();
}
return instance;
}
}
// normale metoder
public void Add(object o)
{
list.Add(o);
}
public ArrayList GetList()
{
return list;
}
}
Klassen kan bruges som følger:
using System;
using System.Collections;
class MainClass
{
public static void Main(string[] args)
{
S3 a = S3.Instance;
a.Add("A");
S3 b = S3.Instance;
b.Add("B");
S3 c = S3.Instance;
ArrayList list = c.GetList();
for(int i = 0; i < list.Count; i++)
{
Console.WriteLine((string)list[i]);
}
}
}
Singleton i multithreaded kontekst
Bemærk at i en multithreaded kontekst bør man kode sin
singleton klasse som:
using System;
using System.Collections;
// singleton klasse
public class S4
{
// normale attributter eksemplificeret ved en ArrayList
private ArrayList list;
// den eneste instans der eksisterer
private static S4 instance = null;
// private constructor
private S4() {
list = new ArrayList();
}
// lock object
private static object mylock = new object();
// property til at hente instance
public static S4 Instance
{
get {
lock(mylock)
{
if(instance == null)
{
instance = new S4();
}
}
return instance;
}
}
// normale metoder
public void Add(object o)
{
list.Add(o);
}
public ArrayList GetList()
{
return list;
}
}
Vær forsigtig med at bruge:
using System;
using System.Collections;
// singleton klasse
public class S5
{
// normale attributter eksemplificeret ved en ArrayList
private ArrayList list;
// den eneste instans der eksisterer
private static S5 instance = null;
// private constructor
private S5() {
list = new ArrayList();
}
// lock object
private static object mylock = new object();
// property til at hente instance
public static S5 Instance
{
get {
if(instance == null)
{
lock(mylock)
{
if(instance == null)
{
instance = new S5();
}
}
}
return instance;
}
}
// normale metoder
public void Add(object o)
{
list.Add(o);
}
public ArrayList GetList()
{
return list;
}
}
For selvom det er blevet hævdet af mange inkl. diverse
Microsoft eksempler, at dette trick kendt som double
locing altid virker i .NET, så er fakta at det kun altid virker
på IA-32 og AMD-64 kompatible CPU'er. Problemet kan løses ved
enten at erklære instance field som volatile eller bruge
Thread.MemoryBarrier, men så er en banal lock nok pænere.
Der er dog ikke mange som kører .NET på IA-64 CPU'er
endnu.
I .NET verdenen er det ildeset med brug af lock(typeof(Sx)) og det
bør derfor undgåes (selvom argumentet måske er lidt tyndt) !
For detaljer om dette læs:
http://blogs.msdn.com/ (...)
http://blogs.gotdotnet.com/ (...)
http://discuss.develop.com/ (...)
og:
http://msdn.microsoft.com/ (...)

