由于特定原因,我必须在我的应用程序中使用两种类型的字典包装器(一种用于值类型,一种用于实例类型),这些类看起来如下:
internal class StructDictionary<K, V> where V : struct
{
public IDictionary<K, V> _dictionary = new Dictionary<K, V>();
public StructDictionary(Dictionary<K, V> dictionary)
{
_dictionary = dictionary;
}
public V? this[K key]
{
get
{
V foundValue;
return _dictionary.TryGetValue(key, out foundValue) ? foundValue : (V?)null;
}
set
{
if (!value.HasValue)
return;
_dictionary[key] = value.Value;
}
}
}
和例如类型
internal class InstanceDictionary<K, V> where V : class
{
public IDictionary<K, V> _dictionary = new Dictionary<K, V>();
public InstanceDictionary(Dictionary<K, V> dictionary)
{
_dictionary = dictionary;
}
public V this[K key]
{
get
{
V foundValue;
return _dictionary.TryGetValue(key, out foundValue) ? foundValue : null;
}
set
{
if (value == null)
return;
_dictionary[key] = value;
}
}
}
这两个类的用法如下:
void Main()
{
var structDictionary = new StructDictionary<string, double>(new Dictionary<string, double>(){{"key1",1}});
var instanceDictionary = new InstanceDictionary<string, string>(new Dictionary<string, string>(){{"key1","value1"}});
structDictionary["key1"] = 70;
instanceDictionary["key1"] = "NEW_VAL";
}
由于2个字典包装器中的逻辑实际上是相同的,我想用只有一个类替换2个类而不影响性能,因为这些对象被广泛使用。
到目前为止,我发现这个非最佳解决方案,我基本上从索引器返回对象。这个解决方案并没有真正起作用,因为发生了一些装箱/拆箱(参见下面代码中的注释),并且它也使用了转速不快的Convert.ChangeType。
internal class BetterDictionary<K, V>
{
public IDictionary<K, V> _dictionary = new Dictionary<K, V>();
public BetterDictionary(Dictionary<K, V> dictionary)
{
_dictionary = dictionary;
}
public object this[K key]
{
get
{
V foundValue;
return _dictionary.TryGetValue(key, out foundValue) ? (object)foundValue /* Issue 1: boxing here when V is value type */: null;
}
set
{
if (value==null)
return;
_dictionary[key] = (V)Convert.ChangeType(value, typeof(V)); // Issue 2: slight performance hit due to ChangeType ?
// more code here
}
}
}
总之,是否有更好的解决方案(更好的BetterDictionary包装器)不会影响性能,基本上我想要一个支持V泛型参数的值类型和实例类型的通用对象。
编辑为了回答这个问题,为什么我需要使用这个有点特殊的字典包装器呢,因为我正在处理一个遗留的UI库,它有一些特殊的怪癖我可以只绑定到具有该形式的索引器的对象,另外我必须为不存在的字典值返回null。基本上我将字典数据转换为在UI中显示为网格列,我无法修改UI,我无法修改内部字典,所以我不得不使用这样的人工包装。 / p>
同样,索引器集{}实际上运行的代码不仅仅是设置字典值,而且这也是我无法使用字典的原因。
对于缺乏上下文的抱歉,我将尝试用更多上下文更新示例。
答案 0 :(得分:2)
您应该修改设计,以便索引器不会将null
用作标记值。
首先,null
是一个完全有效的引用类型值。也许在你的代码中,它从未以这种方式使用过,但在任何情况下都会使重载值过于混乱。在任何正常的IDictionary<TKey, TValue>
实施中,您都可以使用null
作为给定密钥的值。
另一方面,您发布的代码似乎错了。您原来的两个班级和新的&#34;组合的&#34;如果索引器的传递value
为null
,则只需返回实现。但是,如果密钥存在于字典中,这意味着您可以将null
分配给给定密钥,然后如果您检索密钥的值,则会获得非{{{} 1}}值回来。
至少,每个setter应该看起来更像这样(使用值类型版本作为示例):
null
一条评论建议使用(在常规字典中) set
{
if (!value.HasValue)
{
_dictionary.RemoveKey(key);
return;
}
_dictionary[key] = value.Value;
}
作为&#34;未找到&#34;返回值,然后使用具体的default(V)
作为值类型类型参数Nullable<T>
。这可能会有效,但它与使用V
作为&#34;未找到&#34;的哨兵的问题相同。在使用引用类型时有。即对于任何键,它实际上通常是完全有效的字典值。用它代替一个&#34;未找到&#34;值不会使实现混淆并与任何其他字典实现不一致:null
值对于密钥是不合法的;要删除密钥,您需要将其值实际设置为非合法null
值(这只会让事情更加混乱......它不是一个合法的价值,但它仍然是你的东西吗?必须分配给索引器。)
按照设计,在处理值类型时使用null
的选项非常有限。您可以装箱(产生费用),或者您可以使用null
(与任何参考类型不兼容)。
如果您真的必须这样做,我建议您编写自己的Nullable<T>
版本,以允许引用类型:
Nullable<T>
然后,您可以让您的索引器在您的类中具有类型struct NullableEx<T>
{
private bool _hasValue;
private T _value;
public NullableEx(T value)
{
_hasValue = true;
_value = value;
}
public bool HasValue { get { return _hasValue; } }
public T Value { get { return _value; } }
// You can also e.g. add implicit operators to convert
// between T and NullableEx<T>, to implement equality
// and hash code operations, etc.
}
。当密钥不存在时,它可以返回NullableEx<V>
。
来电者需要明确检查new NullableEx<V>()
和HasValue
,这比直接使用Value
更方便。但它会起作用,并且不会产生当前解决方案的运行时间成本。
您的问题遗漏的一个重要问题是,代码如何使用您的包装器实现?很难看出这将是一个广泛使用的重要策略。它太脆弱和/或碎片化,具体取决于您使用的实施版本。所以大概你有一些非常具体的场景,你觉得这种方法很有价值。在我看来,发布描述该特定场景的问题会更有成效,解释为什么正常的基于字典的实现不适合您,并寻求帮助解决 问题,以与您目前采用的方式不同的方式。