我想创建一个最多只能存储一个对象副本的类。这里存储的所有对象将共享相同的基类,我希望能够根据它的类型获取对象。
到目前为止,我已经提出了这个解决方案,但我觉得我在使用Type for the Dictionary键时做错了。
多个模块中使用的基类
interface ISessionVariables { }
用于访问的常见单例类的示例
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;
}
}
}
这样我可以从个别模块中调用它
SessionVariableSingleton.Get<ModuleASessionVars>().PropertyA;
SessionVariableSingleton.Get<ModuleCSessionVars>().PropertyC;
这是存储这种数据结构的可接受方式吗?或者使用List或没有Type键的字典有更好的选择吗?
答案 0 :(得分:4)
是Type
是关键;但线程安全是一个问题 - 在许多方面Hashtable
在线程场景中更好。但是,由于您使用的是泛型,因此有更好的选择: 作弊 :
class SessionVariables {
static class Cache<T> where T : ISessionVariable, new() {
public static readonly ISessionVariable Value = new T();
}
ISessionVariable Get<T>() where T : ISessionVariable, new() {
return Cache<T>.Value;
}
}
现在完全是线程安全的(没有“返回不同的实例”问题)没有任何字典费用。
针对Jon的Hashtable
主题进行编辑:
Dictionary<TKey,TValue>
不保证并发性,因此您需要同步所有访问 - 包括读取,作为执行写入的另一个线程可以打破读者( 可以 强制执行此示例,但与大多数线程竞争一样,很难重现)。
通过合同,Hashtable
保证任意数量的读者是安全的,加上最多只有一位作者。来自MSDN:
Hashtable是线程安全的,可供多个读取器线程和单个写入线程使用。当只有一个线程执行写(更新)操作时,它对多线程使用是线程安全的,如果编写器被序列化为Hashtable,则允许无锁读取。
这意味着您可以执行以下操作:
var val = (SomeType)hash[key];
if(val == null) {
// not there; actually compute / create the value
val = ...
// and store it for the next access
lock(syncLock) {
hash[key] = val; // note: could do double-check here
}
}
return val;
请注意,上面的读取循环不需要任何同步;只需要同步写入。另请注意,由于Hashtable
使用object
,因此当键和值为类(非结构)时效果最佳。
是的,现在存在并发词典 - 但上面的工作得很好。
答案 1 :(得分:2)
有一个问题:它不是线程安全的。鉴于你似乎是通过单例使用它,我想你做需要它是线程安全的。
如果您使用的是.NET 4,那么最好使用ConcurrentDictionary
。否则,添加锁定。
ConcurrentDictionary
代码如下所示:
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());
}
}
(我也尽可能避免单身人士,但这是另一回事。)
编辑:使用类型作为字典键专门解决:是的,没关系。实际上,你可以使用引用相等,因为Type
对象是有效的规范 - 只有一个Type
对象来表示特定AppDomain
中的特定类型,无论你怎么问为了它。 (在使用CodeDOM的动态生成的程序集中,可能不正确...不确定。但对于正常情况应该没问题。)