NameValueCollection 的通用形式






4.97/5 (14投票s)
表示 NameValueCollection 的 C# 通用实现。
目录
引言
NameValueCollection 在 mscorlib 中的实现是一个类似于 Dictionary<string,string> 的集合,但 NameValueCollection 允许重复的键,而 Dictionary<string,string> 不允许。 可以通过索引和键获取元素。
这个集合的特别之处在于,一个键可以包含多个元素,并且允许使用 null 作为键或值。 但存在一个小问题。 NameValueCollection 假定 string 用作键和值。 那么,如果您想要存储任何类型的值,而不仅仅是 string,该怎么办? 当然,您可以在每次获取值时将文本转换为所需的类型,但这里有三个重要的限制:
- 转换的处理开销;
- 并非所有类型都支持与字符串之间的转换;
- 原始对象的引用未保留。
需要在集合中存储原始类型的对象的需求促使我编写 NameValueCollection<T> 的泛型形式,作为 NameValueCollection 的替代方案。
背景
NameValueCollection<T> 集合基于 NameObjectCollectionBase - 关联的 string 键和 object 值的集合的基类,它包含访问这些值的基础方法。 IDictionary,IEnumerable<KeyValuePair<string,T>> 接口在类中实现,以提高可用性。
类定义
首先,定义包含键和值的类及其成员。 私有字段将在指定的数组中包含缓存的数据。 InvalidateCachedArrays
方法将重置缓存,并在每次数据更改时调用。
public partial class NameValueCollection<T> : NameObjectCollectionBase
{
private string[] _keys; // Cached keys.
private T[] _values; // Cached values.
// Resets the caches.
protected void InvalidateCachedArrays()
{
_values = null;
_keys = null;
}
}
基本方法
下一步,是时候添加可以使用基类方法在集合中获取、设置和删除数据的类方法了。
Add
和 Set
方法将接收到的值放入与指定键配对的列表中。
public partial class NameValueCollection<T>
{
// Adds single value to collection.
public void Add(string name, T value)
{
InvalidateCachedArrays();
List<T> list = BaseGet(name) as List<T>;
if (list == null)
{
list = new List<T>(1) {value};
BaseAdd(name, list);
}
else
{
if (value == null) return;
list.Add(value);
}
}
// Adds range of values to collection.
public void Add(NameValueCollection<T> collection)
{
InvalidateCachedArrays();
int count = collection.Count;
for (int i = 0; i < count; i++)
{
string key = collection.GetKey(i);
T[] values = collection.Get(i);
foreach (var value in values)
{
Add(key, value);
}
}
}
// Set single value (previous values will be removed).
public void Set(string name, T value)
{
InvalidateCachedArrays();
BaseSet(name, new List<T>(1){value});
}
// Set range of values (previous values will be removed).
public void Set(string name, params T[] values)
{
InvalidateCachedArrays();
BaseSet(name, new List<T>(values));
}
}
GetKey
和 Get
方法返回请求的键和与键关联的值的数组。 GetAllValues
返回所有值,无论它们与哪些键配对,此方法在将来会很有用。
public partial class NameValueCollection<T>
{
// Gets all values cache.
protected T[] GetAllValues()
{
int count = Count;
List<T> list = new List<T>(count);
for (int i = 0; i < count; ++i)
{
list.AddRange(Get(i));
}
return list.ToArray();
}
// Gets all values that paired with specified key.
public T[] Get(string name)
{
return ((List<T>)BaseGet(name)).ToArray();
}
// Gets all values at the specified index of collection.
public T[] Get(int index)
{
return ((List<T>)BaseGet(index)).ToArray();
}
// Gets string containing the key at the specified index.
public string GetKey(int index)
{
return BaseGetKey(index);
}
}
Clear
和 Remove
方法从集合中删除值。
public partial class NameValueCollection<T>
{
// Removes values from the specified key.
public void Remove(string name)
{
InvalidateCachedArrays();
BaseRemove(name);
}
// Removes all data from the collection.
public void Clear()
{
InvalidateCachedArrays();
BaseClear();
}
}
属性
快完成了! 为了使这个集合更易于使用,最好添加属性。 Keys
和 Values
属性尝试返回缓存的数据,如果缓存失效,则更新它。
public partial class NameValueCollection<T>
{
// All keys that the current collection contains.
public string[] Keys
{
get
{
if (_keys == null)
_keys = BaseGetAllKeys();
return _keys;
}
}
// All values that the current collection contains.
public T[] Values
{
get
{
if (_values == null)
_values = GetAllValues();
return _values;
}
}
// Values at the specified index.
public T[] this[int index]
{
get
{
return Get(index);
}
set
{
BaseSet(index, new List<T>(value));
}
}
// Values at the specified key.
public T[] this[string name]
{
get
{
return Get(name);
}
set
{
BaseSet(name, new List<T>(value));
}
}
}
集合枚举
嵌入式类 Enumerator
将负责枚举集合中的所有键值对。 GetAllEntries
方法返回枚举器使用的所有键值对。
public partial class NameValueCollection<T>
{
// Enumerates all entries.
protected IEnumerable<KeyValuePair<string, T>> GetAllEntries()
{
return
from key in Keys
from value in Get(key)
select new KeyValuePair<string, T>(key, value);
}
// The enumerator that can enumerate all entries in the collection.
private class Enumerator : IEnumerator<KeyValuePair<string, T>>, IDictionaryEnumerator
{
// Enumerate all entries in collection.
private readonly IEnumerator<KeyValuePair<string, T>> _enumerator;
// Initialize the enumerator.
public Enumerator(NameValueCollection<T> collection)
{
IEnumerable<KeyValuePair<string, T>> entries =
collection.GetAllEntries().ToArray();
_enumerator = entries.GetEnumerator();
}
// Returns the element of the collection corresponding
// to the current position of the enumerator.
KeyValuePair<string, T> IEnumerator<KeyValuePair<string, T>>.Current
{
get { return _enumerator.Current; }
}
// Returns the object of the collection corresponding
// to the current position of the enumerator.
object IEnumerator.Current
{
get
{
IEnumerator enumerator = ((IEnumerator) _enumerator);
return enumerator.Current;
}
}
// Gets the key of the current dictionary entry.
object IDictionaryEnumerator.Key
{
get
{
IEnumerator<KeyValuePair<string, T>> enumerator =
((IEnumerator<KeyValuePair<string, T>>) this);
return enumerator.Current.Key;
}
}
// Gets the value of the current dictionary entry.
object IDictionaryEnumerator.Value
{
get
{
IEnumerator<KeyValuePair<string, T>> enumerator =
((IEnumerator<KeyValuePair<string, T>>) this);
return enumerator.Current.Value;
}
}
// Gets both the key and the value of the current dictionary entry.
DictionaryEntry IDictionaryEnumerator.Entry
{
get
{
IEnumerator<KeyValuePair<string, T>> enumerator =
((IEnumerator<KeyValuePair<string, T>>) this);
return new DictionaryEntry(enumerator.Current.Key,
enumerator.Current.Value);
}
}
// Move enumerator to the next entry.
public bool MoveNext()
{
return _enumerator.MoveNext();
}
// Reset enumerator to start position.
public void Reset()
{
_enumerator.Reset();
}
// Unused dispose pattern.
public void Dispose()
{
}
}
}
最后一步是实现指定的接口 IDictionary 和 IEnumerable<KeyValuePair<string,T>>。 某些方法和属性是显式实现的,也就是说,它们的使用需要先强制转换为适当的接口类型。
public partial class NameValueCollection<T> :
IDictionary,
IEnumerable<KeyValuePair<string, T>>
{
// Gets an collection containing the values.
ICollection IDictionary.Keys => Keys;
// Gets an collection containing the values.
ICollection IDictionary.Values => Values;
// Indicates whether the collection is read-only.
public new bool IsReadOnly => base.IsReadOnly;
// Indicates whether the collection contains a fixed number of items.
public bool IsFixedSize => false;
// Determines whether the collection contains an element with the specified key.
bool IDictionary.Contains(object key)
{
return key is string s && Keys.Contains(s);
}
// Adds an object with the specified key to the colletion.
void IDictionary.Add(object key, object value)
{
Add((string)key, (T)value);
}
// Removes the element with the specified key from the collection.
void IDictionary.Remove(object key)
{
Remove((string)key);
}
// Gets or sets the item with the specified key.
object IDictionary.this[object key]
{
get
{
return Get((string)key);
}
set
{
if (value is IEnumerable<T> collection)
{
Set((string)key, (T[])collection.ToArray());
}
else
{
Set((string)key, (T)value);
}
}
}
// The three methods below return an enumerator for current collection.
IEnumerator<KeyValuePair<string, T>> IEnumerable<KeyValuePair<string, T>>.GetEnumerator()
{
return new Enumerator(this);
}
public override IEnumerator GetEnumerator()
{
return new Enumerator(this);
}
IDictionaryEnumerator IDictionary.GetEnumerator()
{
return new Enumerator(this);
}
}
用法
NameValueCollection<int> collection = new NameValueCollection<int>();
collection.Add("a", 123);
collection.Add("a", 456); // 123 and 456 will be inserted into the same key.
collection.Add("b", 789); // 789 will be inserted into another key.
int[] a = collection.Get("a"); // contains 123 and 456.
int[] b = collection.Get("b"); // contains 789.
在本文的结尾,我想说上面的代码实现了基本功能。 在附件中,您将找到包含本文未包含的一些其他扩展的完整源代码。
历史
- 2022年1月26日:初始版本
- 2022年2月5日:ArrayList 已被 List 替换。