可以将类型用作字典键吗?

Rac*_*hel 6 c# dictionary .net-3.5 data-structures

我想创建一个最多可以存储一个对象副本的类。此处存储的所有对象将共享相同的基类,我希望能够根据其类型获取对象。

到目前为止,我已经提出了这个解决方案,但是我觉得使用Type作为Dictionary键会出错。

在多个模块中使用的基类

interface ISessionVariables { }
Run Code Online (Sandbox Code Playgroud)

用于访问的普通单例类的示例

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;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

这样我可以从各个模块中这样称呼它

SessionVariableSingleton.Get<ModuleASessionVars>().PropertyA;

SessionVariableSingleton.Get<ModuleCSessionVars>().PropertyC;
Run Code Online (Sandbox Code Playgroud)

这是存储这种数据结构的可接受方法吗?还是有一个更好的选择,使用没有类型键的列表或字典?

Jon*_*eet 6

有一个问题:它不是线程安全的。鉴于您似乎通过单例使用它,我想您确实需要它是线程安全的。

如果您使用 .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());
    }
}
Run Code Online (Sandbox Code Playgroud)

(我也会尽可能避免单身人士,但那是另一回事。)

编辑:专门解决使用类型作为字典键的问题:是的,没关系。事实上,您对引用相等没问题,因为Type对象实际上是规范的 -无论您如何要求,都只有一个Type对象来表示特定 中的特定类型AppDomain。(这在使用 CodeDOM 动态生成的程序集中可能不是真的……不确定。但对于正常情况应该没问题。)


Mar*_*ell 5

是的Type是罚款作为重点; 但是,线程安全是一个问题-在许多方面,线程安全性要Hashtable好得多。但是,有一个更好的选择,因为您使用的是泛型:cheat

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;
    }
}
Run Code Online (Sandbox Code Playgroud)

现在,它是完全线程安全的(没有“返回不同的实例”的问题),而没有任何字典成本。


编辑有关HashtableJon 的主题:

Dictionary<TKey,TValue> makes no guarantees on concurrency, so you are required to synchronize all access - including the reads, as another thread doing a write can break a reader (you can force this in an example, but like most thread-races, it is hard to reproduce).

By contract, Hashtable guarantees that it is safe for any number of readers, plus at most one writer. From MSDN:

Hashtable is thread safe for use by multiple reader threads and a single writing thread. It is thread safe for multi-thread use when only one of the threads perform write (update) operations, which allows for lock-free reads provided that the writers are serialized to the Hashtable.

This means that you can do things like:

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;
Run Code Online (Sandbox Code Playgroud)

请注意,上面的读取周期不需要任何同步。只有写入需要同步。还请注意,因为Hashtable使用object,所以当键和值是类(而不是结构)时,效果最佳。

是的,现在存在并发词典-但以上方法工作正常

  • @Jon,当您说“我不知道”时,我们真的需要创建新的金牌。我怀疑,我们不需要经常授予它; p (3认同)