是否可以使用类型作为字典键?

时间:2013-11-19 14:06:18

标签: c# data-structures dictionary .net-3.5

我想创建一个最多只能存储一个对象副本的类。这里存储的所有对象将共享相同的基类,我希望能够根据它的类型获取对象。

到目前为止,我已经提出了这个解决方案,但我觉得我在使用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键的字典有更好的选择吗?

2 个答案:

答案 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的动态生成的程序集中,可能不正确...不确定。但对于正常情况应该没问题。)