I would like to make a class that can store at most one copy of an object. All the objects stored here will share the same base class, and I would like to be able to get an object based on it's type.
I've come up with this solution so far, but I feel like I'm doing something wrong with using a Type for the Dictionary key.
Base class used in multiple modules
interface ISessionVariables { }
Example of common singleton class used for accessing
public class SessionVariables
{
private object _sync = new object();
private Dictionary<Type, ISessionVariables> _sessionVariables =
new Dictionary<Type, ISessionVariables>;
public T Get<T>()
where T : ISessionVariable, new()
{
lock (_sync)
{
ISessionVariables rtnValue = null;
if (_sessionVariables.TryGetValue(typeof(T), out rtnValue))
return (T)rtnValue;
rtnValue = new T();
_sessionVariables.Add(typeof(T), rtnValue);
return (T)rtnValue;
}
}
}
This way I can call it like this from individual modules
SessionVariableSingleton.Get<ModuleASessionVars>().PropertyA;
SessionVariableSingleton.Get<ModuleCSessionVars>().PropertyC;
Is this an acceptable way of storing this kind of data structure? Or is there a better alternative using a List or a dictionary without a Type key?
There's one issue: it's not thread-safe. Given that you appear to be using this via a singleton, I'd imagine you do need it to be thread-safe.
If you're using .NET 4, you'd be best off using ConcurrentDictionary
. Otherwise, add locking.
The ConcurrentDictionary
code would look like this:
internal class SessionVariables
{
private readonly ConcurrentDictionary<Type, ISessionVariables> dictionary
= new ConcurrentDictionary<Type, ISessionVariables>();
internal ISessionVariable Get<T>() where T : ISessionVariable, new()
{
return dictionary.GetOrAdd(typeof(T), _ => new T());
}
}
(I'd also avoid singletons where possible, but that's a different matter.)
EDIT: To specifically address using a type as a dictionary key: yes, that's fine. In fact, you're fine with reference equality, as Type
objects are effectively canonically - there's only one Type
object to represent a particular type in a particular AppDomain
, no matter how you ask for it. (This may not be true in dynamically generated assemblies using CodeDOM... not sure. But it should be fine for the normal case.)
See more on this question at Stackoverflow