我正在设计一种模板语言。其中有3种标记:标记,指令和变量。这些令牌中的每一个都有一个名称,而且它们中有相当多。它们也是可扩展的。
要允许名称重用,我想添加名称空间。
现在所有变量都只存储在一个字典中。键是变量名,值是变量值。这样我就可以快速检索变量的值。但是,假设我想允许点符号namespace.variable
,我如何存储这些变量,以便命名空间是可选的?如果包含名称空间,则dict应该只扫描该名称空间,如果没有,我猜它会扫描所有名称空间。
是否有容器可以执行此操作?
答案 0 :(得分:2)
您应该在内部将符号数据构造为字符串字典的字典。顶级字典用于名称空间,每个名称空间名称下面的每个字典都是该名称空间中所有符号的容器。
查找不合格的符号只需要按特定顺序查找每个命名空间中的符号。在C#或Delphi中,顺序由声明命名空间在源文件顶部的顺序决定,与声明的顺序相反(最近的是第一个被搜索的)。
答案 1 :(得分:1)
您可以创建自己的IDictionary<string, object>
实现,而不是使用框架的Dictionary<TKey, TValue>
。
在外部,你消费它的方式没有变化。
在内部,它将包含Dictionary<string, Dictionary<string, object>>
。
因此,如果要求您的字典输入值匹配键"namespace.variable"
,则会在内部拆分该字符串,使用键“namespace”获取Dictionary<string, Dictionary<string, object>>
,然后返回{{1}中的值密钥“变量”。
要使命名空间可选,您有一个条目,其中键为Dictionary<string, object>
。无论是添加还是获取项目,只要提供的密钥不包含string.Empty
,您就会使用密钥为.
的条目。
答案 2 :(得分:1)
我的解决方案:
public class NamespaceDictionary<T> : IDictionary<string, T>
{
private SortedDictionary<string, Dictionary<string, T>> _dict;
private const char _separator = '.';
public NamespaceDictionary()
{
_dict = new SortedDictionary<string, Dictionary<string, T>>();
}
public NamespaceDictionary(IEnumerable<KeyValuePair<string, T>> collection)
: this()
{
foreach (var item in collection)
Add(item);
}
#region Implementation of IEnumerable
public IEnumerator<KeyValuePair<string, T>> GetEnumerator()
{
return _dict.SelectMany(x => x.Value).GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
#endregion
private static Tuple<string, string> Split(string name)
{
int pos = name.LastIndexOf(_separator);
string ns = pos == -1 ? "" : name.Substring(0, pos);
string var = name.Substring(pos + 1);
return new Tuple<string, string>(ns, var);
}
#region Implementation of ICollection<KeyValuePair<string,TValue>>
public void Add(KeyValuePair<string, T> item)
{
Add(item.Key, item.Value);
}
public void Clear()
{
_dict.Clear();
}
public bool Contains(KeyValuePair<string, T> item)
{
throw new NotImplementedException();
}
public void CopyTo(KeyValuePair<string, T>[] array, int arrayIndex)
{
throw new NotImplementedException();
}
public bool Remove(KeyValuePair<string, T> item)
{
return Remove(item.Key);
}
public int Count
{
get { return _dict.Sum(p => p.Value.Count); }
}
public bool IsReadOnly
{
get { return false; }
}
#endregion
#region Implementation of IDictionary<string,TValue>
public bool ContainsKey(string name)
{
var tuple = Split(name);
return ContainsKey(tuple.Item1, tuple.Item2);
}
public bool ContainsKey(string ns, string key)
{
if (ns == "")
return _dict.Any(pair => pair.Value.ContainsKey(key));
return _dict.ContainsKey(ns) && _dict[ns].ContainsKey(key);
}
public void Add(string name, T value)
{
var tuple = Split(name);
Add(tuple.Item1, tuple.Item2, value);
}
public void Add(string ns, string key, T value)
{
if (!_dict.ContainsKey(ns))
_dict[ns] = new Dictionary<string, T>();
_dict[ns].Add(key, value);
}
public bool Remove(string ns, string key)
{
if (_dict.ContainsKey(ns) && _dict[ns].ContainsKey(key))
{
if (_dict[ns].Count == 1) _dict.Remove(ns);
else _dict[ns].Remove(key);
return true;
}
return false;
}
public bool Remove(string key)
{
var tuple = Split(key);
return Remove(tuple.Item1, tuple.Item2);
}
public bool TryGetValue(string name, out T value)
{
var tuple = Split(name);
return TryGetValue(tuple.Item1, tuple.Item2, out value);
}
public bool TryGetValue(string ns, string key, out T value)
{
if (ns == "")
{
foreach (var pair in _dict)
{
if (pair.Value.ContainsKey(key))
{
value = pair.Value[key];
return true;
}
}
}
else if (_dict.ContainsKey(ns) && _dict[ns].ContainsKey(key))
{
value = _dict[ns][key];
return true;
}
value = default(T);
return false;
}
public T this[string ns, string key]
{
get
{
if (ns == "")
{
foreach (var pair in _dict)
if (pair.Value.ContainsKey(key))
return pair.Value[key];
}
else if (_dict.ContainsKey(ns) && _dict[ns].ContainsKey(key))
return _dict[ns][key];
throw new KeyNotFoundException();
}
set
{
if (!_dict.ContainsKey(ns))
_dict[ns] = new Dictionary<string, T>();
_dict[ns][key] = value;
}
}
public T this[string name]
{
get
{
var tuple = Split(name);
return this[tuple.Item1, tuple.Item2];
}
set
{
var tuple = Split(name);
this[tuple.Item1, tuple.Item2] = value;
}
}
public ICollection<string> Keys
{
get { return _dict.SelectMany(p => p.Value.Keys).ToArray(); }
}
public ICollection<T> Values
{
get { return _dict.SelectMany(p => p.Value.Values).ToArray(); }
}
#endregion
}
var dict = new NamespaceDictionary<int>();
dict.Add("ns1.var1", 1);
dict.Add("ns2.var1", 2);
dict.Add("var2", 3);
dict.Add("ns2.var2", 4);
dict.Add("ns3", "var1", 5);
dict["ns4.var1"] = 6;
Console.WriteLine(dict["var1"]);
Console.WriteLine(dict["ns2.var1"]);
Console.WriteLine(dict["var2"]);
Console.WriteLine(dict["ns2.var2"]);
Console.WriteLine(dict["ns2", "var2"]);
Console.WriteLine(dict["ns3.var1"]);
Console.WriteLine(dict["ns4", "var1"]);
1
2
3
4
4
5
6
我使用SortedDictionary
认为它会保留添加名称空间的顺序,但它实际上是按字母顺序对名称空间进行排序。是否有一个dict类将保留添加项目的顺序,但不对它们进行排序?