|
I've been writing some code that calls for a single instance of a class per geodatabase workspace. This is important when trying to maintain things like an Identity Map – you don't want multiple instances of a class that access the same geodatabase floating around. So, I started with the GoF Singleton pattern:
public class Singleton { private static Singleton _instance;
private Singleton() { }
public static Singleton Instance() { if (_instance == null) { _instance = new Singleton(); }
return _instance; } }
This pattern creates one and only one instance of the Singleton class within the application. However, I want one and only one instance for each Workspace that I open. When I query for an instance of the Singleton, I need to pass in the Workspace that corresponds to the Singleton instance:
public static Singleton Instance(IWorkspace workspace)
Internally, I defined a Dictionary that maintains a mapping of instances of the Singleton objects and their corresponding Workspaces. This mapping is maintained for all instances of the Singleton class, and so is a static member:
private static Dictionary<IWorkspace, Singleton> _instances = new Dictionary<IWorkspace, Singleton>();
Each Singleton instance maintains a reference to its own Workspace:
private IWorkspace _workspace;
When a client calls the Instance method and passes a Workspace, the Instance method checks the Dictionary to see if an instance for the Workspace already exists; if it does, then that instance is returned; if not, then a new instance is created for that Workspace and returned.
Following is the complete code for the implementation of the Singleton per Workspace:
public class Singleton { private static Dictionary<IWorkspace, Singleton> _instances = new Dictionary<IWorkspace, Singleton>(); private static readonly object _lock = new object(); private IWorkspace _workspace;
private Singleton(IWorkspace workspace) { _workspace = workspace; }
public static Singleton Instance(IWorkspace workspace) { lock (_lock) { if (_instances.ContainsKey(workspace)) { return _instances[workspace]; }
Singleton instance = new Singleton(workspace); _instances.Add(workspace, instance);
return instance; } } }
It's important to note that we have the right test for identity. In this example, I know that I'm using the same Workspace object to create a Singleton instance, so I can use the identity operator. If I'm not sure of that, then I would need to devise some scheme to test for identity.
So, let's make this generic for any singleton ArcObjects type:
public class Singleton<T> { private static Dictionary<T, Singleton<T>> _instances = new Dictionary<T, Singleton<T>>(); private static readonly object _lock = new object(); private T _internalObject;
private Singleton(T internalObject) { _internalObject = internalObject; }
public static Singleton<T> Instance(T internalObject) { lock (_lock) { if (_instances.ContainsKey(internalObject)) { return _instances[internalObject]; }
Singleton<T> instance = new Singleton<T>(internalObject); _instances.Add(internalObject, instance);
return instance; } } }
|