When you are programming your application using the ArcGIS ArcObjects API it’s best to remain aware of classes which support only one instance across your application or in each thread. This type of class is called a “singleton”. The coding techniques for how objects of this type of class are created, used, and disposed of are a little different than other object classes. This topic is sometimes a little confusing, and carries with it its own idiosyncracies, so we wanted to bring this up, describe it, and see if the developer community out there have any thoughts or helpful hints we can all share with each other.
Advanced coders might see this topic are fairly basic, but we at ESRI have seen difficulties and a bit of confusion come up often enough on our developer support help hotline and from students in our instructor-led developer training classes, that we figured it might be a good topic to discuss here.
Typically a co-createable class gives you the flexibility to create as many instances as your application design needs. Singleton classes are an important exception. A list of which classes are used as singletons (versions 9.2 and 9.3) can be found here:
Looking through the list, the reason why some of these classes are limited to singleton usage might be fairly obvious. For example the AppRef, MonitorSettings, or SystemHelper classes. But others may not be so obvious, like the StyleGallery, FindDialog, and the various ~WorkspaceFactory and ~ToolbarEnvironment classes, so watch out for them because you’ll use and manage singletons differently than other class instances you create.
Singletons, how and why
How it works is this. A singleton class declares the class constructor as private so that no other object can create a new instance and prevent other objects from instantiating their own copies of the singleton object, ensuring that all operations throughout your application access the single instance. Using the proper instantiation mechanism and by providing a central mechanism by which all objects can obtain a reference to the single instance, it is possible to manage your use of singleton objects correctly, keeping full control over the stability and memory usage of your application.
singletons: you can only eat one...
Creating and managing singleton objects
Even though the special techniques for using singletons are fairly straightforward, there are few important things to understand, otherwise, singletons are left dangling (called pinning). If you have ever received this runtime error:
“Unable to cast object of type System.__ComObject to type <Typename>”
...good chance you are trying to create a new instance of a singleton class that already exists. Bad news. But easy to fix.
The following piece of code raises an error with Visual Basic’s New keyword as the .NET Framework is unable to wrap in a strongly typed RCW an instance of an object that has previously been wrapped in the generic System.__ComObject RCW.
[VB.NET]
‘The following line of code is incorrect and generates an "Unable to cast..." runtime error.
Dim sg As ESRI.ArcGIS.Display.IStyleGallery = New ESRI.ArcGIS.Framework.StyleGalleryClass
Singleton classes can only be used by using the Activator class. This class provides a CreateInstance method which you will use to create a singleton objects and other variable references to it. Here is one proper way to make it work:
[VB.NET]
‘The proper way to use singleton object classes
Dim t As Type = Type.GetTypeFromProgID("esriFramework.StyleGallery")
Dim obj As System.Object = Activator.CreateInstance(t)
Dim pApp As ESRI.ArcGIS.Display.IStyleGallery = obj
As you can see, a little different than creating objects using other classes.
Releasing singleton objects
Now that you have created a singleton object or additional references to it, any object variables in scope which reference singletons must eventually be explicitly released using the ComReleaser class.
The following example code shows how you can call the ReleaseComObject method to release a StyleGallery object.
[VB.NET]
Dim refsLeft As Integer = 0
Do
refsLeft = System.Runtime.InteropServices.Marshal.ReleaseComObject(pApp)
Loop While (refsLeft > 0)
The code follows a loop that calls ReleaseComObject until the returned value is zero. This indicates there are no longer any managed references to the StyleGallery, and such code should only be used when you are sure no other managed code will require further access to the object. The ComReleaser class can be found in the ESRI.ArcGIS.ADF namespace.
For more info, or for just fun reading
1. Here is some more information and MSDN documentation about .NET's System.Activator class.
2. If your application design includes creating singleton classes of your own, here's a clever and entertaining blog rant called "Singleton Considered Stupid" from Steve Yegge about how singletons are used and often misused nowadays.
Thoughts? Ideas? Questions anyone?