我有一组枚举值(准确的故障代码)。代码是16位无符号整数。我正在寻找一个可以代表这种枚举的数据结构。这里也提出了类似的问题:What's the best C# pattern for implementing a hierarchy with an enum?。但这种等级制度更深。
样本枚举值
Current = 0x2000,
Current_DeviceInputSide = 0x2100,
ShortToEarth = 0x2120,
ShortToEarthInPhase1 = 0x2121,
ShortToEarthInPhase2 = 0x2122,
ShortToEarthInPhase3 = 0x2123
用例
当用户提供代码时,UI必须显示具有层次结构的代码的等效含义。
例如,如果用户提供值0x2121
,则UI必须显示Short to earth in phase 1 in the current at device input side
。表示此问题的最佳方式是使用分层表示法:Current : DeviceInputSide : ShortToEarth : ShortToEarthInPhase1
。
竞争方法
我有三种竞争方法来表示枚举:
方法1
枚举:
enum WarnCodes
{
None= 0x000,
Current = 0x2000
}
enum WarnCodes_Current
{
DeviceInputSide = 0x2100,
DeviceOutputSide = 0x2200
}
enum WarnCodes_Current_DeviceInputSide
{
ShortToEarth = 0x2120,
ShortCircuit = 0x2130
}
enum WarnCodes_Current_DeviceInputSide_ShortToEarth
{
InPhase1 = 0x2121,
InPhase2 = 0x2122
}
控制器:
public string GetMeaning(int code)
{
int bitMask = 0xF000;
int maskedCode = bitMask & code;
StringBuilder meaning = new StringBuilder();
switch (maskedCode)
{
case WarnCodes.Current:
meaning.Append("Current : ");
bitMask = 0xFF00;
maskedCode = bitMask & code;
switch (maskedCode)
{
case WarnCodes_Current.DeviceInputSide:
meaning.Append("Current : Device Input Side :");
...
break;
}
break;
...
}
}
方法2
存储枚举值的xml如下所示
<WarnCodes>
<code hex="2000" meaning="Current">
<code hex="2100" meaning="Current, Device Input side">
<code hex="2120" meaning="Short to Earth">
<code hex="2121" meaning="Short to earth in Phase L1"/>
<code hex="2122" meaning="Short to earth in Phase L2"/>
</code>
</code>
</code>
</WarnCodes>
用于查询代码的方法是:
XElement rootElement = XElement.Load(settingsFilePath);
public string GetHierarchicalMeaning(int code)
{
XElement rootElement = XElement.Load(warnCodesFilePath);
List<string> meanings = new List();
StringBuilder stringBuilder = new StringBuilder();
IEnumerable<XElement> elements;
elements = from el in rootElement.Descendants("code")
where (string)el.Attribute("hex") == code.ToString("X")
select el;
XElement element = elements.First();
while (element.Parent != null)
{
meanings.Add(element.Attribute("meaning").Value);
element = element.Parent;
}
meanings.Reverse();
foreach (string meaning in meanings)
{
stringBuilder.AppendFormat("{0} : ", meaning);
}
return stringBuilder.ToString().Trim().TrimEnd(':').Trim();
}
方法3
存储枚举值的xml与方法2 中的相同。字典由xml
GetChildren()
填充。
private Dictionary<int, WarnCodeValue> warnCodesDictionary;
public void Initialize()
{
XElement rootElement = XElement.Load(settingsFilePath);
warnCodesDictionary = GetChildren(rootElement);
}
private Dictionary<int, WarnCodeValue> GetChildren(XElement element)
{
if (element.Descendants().Count() > 0)
{
Dictionary<int, WarnCodeValue> childNodeDictionary = new Dictionary();
foreach (XElement childElement in element.Elements())
{
int hex = Convert.ToInt32(childElement.Attribute("hex").Value, 16);
string meaning = childElement.Attribute("meaning").Value;
Dictionary<int, WarnCodeValue> dictionary = GetChildren(childElement);
WarnCodeValue warnCodeValue;
if (dictionary == null)
{
warnCodeValue = new WarnCodeValue() {Meaning = meaning};
}
else
{
warnCodeValue = new WarnCodeValue() {Meaning = meaning, ChildNodes = dictionary};
}
childNodeDictionary.Add(hex, warnCodeValue);
}
return childNodeDictionary;
}
return null;
}
使用GetHierarchicalMeaning()
检索含义:
public string GetHierarchicalMeaning(int code)
{
StringBuilder stringBuilder = new StringBuilder();
int firstLevel = code & 0xF000;
int secondLevel = code & 0xFF00;
int thirdLevel = code & 0xFFF0;
if(warnCodesDictionary.ContainsKey(firstLevel))
{
stringBuilder.AppendFormat("{0} : ", warnCodesDictionary[firstLevel].Meaning);
if (warnCodesDictionary[firstLevel].ChildNodes != null &&
warnCodesDictionary[firstLevel].ChildNodes.ContainsKey(secondLevel))
{
stringBuilder.AppendFormat("{0} : ", warnCodesDictionary[firstLevel].ChildNodes[secondLevel].Meaning);
if (warnCodesDictionary[firstLevel].ChildNodes[secondLevel].ChildNodes != null &&
warnCodesDictionary[firstLevel].ChildNodes[secondLevel].ChildNodes.ContainsKey(thirdLevel))
{
stringBuilder.AppendFormat("{0} : ",
warnCodesDictionary[firstLevel].ChildNodes[secondLevel].ChildNodes[thirdLevel].Meaning);
if (warnCodesDictionary[firstLevel].ChildNodes[secondLevel].ChildNodes[thirdLevel].ChildNodes != null &&
warnCodesDictionary[firstLevel].ChildNodes[secondLevel].ChildNodes[thirdLevel].ChildNodes.ContainsKey(code))
{
stringBuilder.AppendFormat("{0} : ",
warnCodesDictionary[firstLevel].ChildNodes[secondLevel].ChildNodes[thirdLevel].ChildNodes[code].Meaning);
}
}
}
}
}
WarnCodeValue
类:
class WarnCodeValue
{
public string Meaning
{ get; set; }
public Dictionary<int, WarnCodeValue> ChildNodes { get; set; }
}
问题
答案 0 :(得分:4)
考虑使用类而不是枚举,然后为每个值使用单例,并且可以使用类型系统构建树,包括生成错误txt的虚拟方法等。(这可以有时候是一个不错的选择,但如果不合适的话,也会引导你进入很多问题
答案 1 :(得分:1)
您可以使用FlagsAttribute。 例如,你可以这样做:
[FlagsAttribute]
enum WarnCodes
{
None= 0x0000,
Current = 0x2000,
// second level of hierarchy
DeviceInputSide = 0x0100,
DeviceOutputSide = 0x0200,
// third level of hierarchy
ShortToEarth = 0x0020,
ShortCircuit = 0x0030,
// fourth level of hierarchy
InPhase1 = 0x0001,
InPhase2 = 0x0002
}
你可以这样测试:
int[] testVals = {0x0000, 0x2000, 0x2130, 0x2122, 0x2121, 0x2131};
foreach(var val in testVals)
{
Console.WriteLine( "{0,4:X} - {1}",
val, ( (WarnCodes)val ).ToString( ) );
}
答案 2 :(得分:1)
第二次尝试......您可以实现自己的树结构,其中每个节点都有一位十六进制表示,而像0x2121这样的代码代表树的一个分支:
>2 - (current)
/ \
(device input side)>1 2 (device output side)
/\ /\
>2 (short to earth)
/\
>1 (in phase 1)
因此,要读取0x2121的含义,我们将遵循树的相应分支,并且(对于每个节点)我们读取它包含的消息。
这是一个快速而又脏的树实现:
public class TreeNode
{
private List<TreeNode> _children;
public int hex {get; private set;}
public string meaning {get; private set;}
public IList<TreeNode> children {
get{
return _children.AsReadOnly();
}
}
public TreeNode(int hex, string meaning)
{
this.hex = hex;
this.meaning = meaning;
_children = new List<TreeNode>();
}
public TreeNode addChild(int hex, string meaning)
{
if(hex<=0 || hex >=16) throw new ArgumentOutOfRangeException("hex");
if(GetChildByCode(hex)!=null) throw new Exception("a child with code " +
hex.ToString() + " already exists");
var child = new TreeNode(hex,meaning);
_children.Add(child);
return child;
}
public TreeNode TryAddChild(int hex, string meaning)
{
if(hex<=0 || hex >=16) throw new ArgumentOutOfRangeException("hex");
var chd = GetChildByCode(hex);
if(chd==null) {
chd = new TreeNode(hex,meaning);
_children.Add(chd);
}
return chd;
}
public void AddBranch(int hexPath, string[] meanings)
{
var lst = intToList(hexPath,16,new LinkedList<int>()).ToList();
var curNode = this;
for(int i = 0; i<lst.Count; i++)
{
curNode = curNode.TryAddChild(lst[i], meanings[i]);
}
}
public TreeNode GetChildByCode(int hex)
{
return
(from c in _children
where c.hex == hex
select c).SingleOrDefault();
}
public string getMessagesByPath(int hexPath)
{
var lst = intToList(hexPath,16,new LinkedList<int>());
var msgs = getMessagesByPath(lst, new List<string>(),this);
return
(msgs == null || msgs.Count==0) ?
"None":
msgs.Aggregate((s1, s2) => s1 + ": " + s2);
}
// recursively follow the branch and read the node messages
protected IList<string> getMessagesByPath(LinkedList<int> hexPath, IList<string> accString, TreeNode curNode)
{
if(hexPath.Count == 0 || hexPath.First.Value == 0 || curNode==null)
return accString;
else
{
var chd = curNode.GetChildByCode(hexPath.First.Value);
string meaning = (chd==null)? "not found": chd.meaning;
accString.Add(meaning);
hexPath.RemoveFirst();
return getMessagesByPath(hexPath,accString,chd);
}
}
// convert the code to a list of digits in the given base (in this case 16)
// this could be an extension method for int
private LinkedList<int> intToList(int theInt, int theBase, LinkedList<int> acc)
{
if(theInt < theBase)
{
acc.AddFirst(theInt);
return acc;
}
else
{
acc.AddFirst(theInt % theBase);
return intToList(theInt/theBase, theBase, acc);
}
}
}
你可以这样填充树:
var root = new TreeNode(0,"root");
root.AddBranch(0x2121, new string[] {"Current", "DeviceInputSide", "Short to Earth", "In phase I"});
root.AddBranch(0x2122, new string[] {"Current", "DeviceInputSide", "Short to Earth", "In phase II"});
root.AddBranch(0x2123, new string[] {"Current", "DeviceInputSide", "Short to Earth", "In phase III"});
root.AddBranch(0x2221, new string[] {"Current", "DeviceOutputSide", "Short to Earth", "In phase I"});
root.AddBranch(0x2222, new string[] {"Current", "DeviceOutputSide", "Short to Earth", "In phase II"});
root.AddBranch(0x2223, new string[] {"Current", "DeviceOutputSide", "Short to Earth", "In phase III"});
// ...
通过这种方式,您可以完全控制代码的层次结构,并且可以实现检查,以便结构本身不会被破坏。搜索消息仍然很容易(因为它在第一个0之后不处理代码),搜索0x2000应该更有效,因为实际上只处理了2个。
//search meaning of path
root.getMessagesByPath(0x2122)
答案 3 :(得分:0)
发现方法3 的修改版本是最合适的。感谢 @paolo 帮助我找到答案。
修改方法3
包含代码的xml
:
<?xml version="1.0" encoding="utf-8" ?>
<WarnCodes>
<code hex="2000" meaning="Current">
<code hex="2100" meaning="Current, Device Input side">
<code hex="2120" meaning="Short to Earth">
<code hex="2121" meaning="Short to earth in Phase L1"/>
<code hex="2122" meaning="Short to earth in Phase L2"/>
</code>
</code>
</code>
<code hex="3000" meaning="Voltage"/>
</WarnCodes>
WarnCodeValue
类:
class WarnCodeValue
{
public string Meaning
{ get; set; }
public string ConcatenatedMeaning
{ get; set; }
public Dictionary<int, WarnCodeValue> ChildNodes
{ get; set; }
}
singleton
处理器类(用于检索代码的含义):
sealed class WarnCodeProcessor
{
private static Dictionary<int, WarnCodeValue> warnCodesDictionary;
private static volatile WarnCodeProcessor _instance;
private static object instanceLockCheck = new object();
public static WarnCodeProcessor Instance
{
get
{
lock (instanceLockCheck)
{
if (_instance == null)
{
_instance = new WarnCodeProcessor();
}
}
return _instance;
}
}
private WarnCodeProcessor()
{
warnCodesDictionary = new Dictionary<int, WarnCodeValue>();
string currentDirectory = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
string settingsFilePath = Path.Combine(currentDirectory, "WarnCodes.xml");
XElement rootElement = XElement.Load(settingsFilePath);
warnCodesDictionary = GetChildren(rootElement, string.Empty);
}
public string GetConcatenatedMeaning(int code)
{
string concatenatedMeaning = string.Empty;
int firstLevel = code & 0xF000;
int secondLevel = code & 0xFF00;
int thirdLevel = code & 0xFFF0;
if (warnCodesDictionary.ContainsKey(firstLevel))
{
concatenatedMeaning = warnCodesDictionary[firstLevel].ConcatenatedMeaning;
if (warnCodesDictionary[firstLevel].ChildNodes != null &&
warnCodesDictionary[firstLevel].ChildNodes.ContainsKey(secondLevel))
{
concatenatedMeaning =
warnCodesDictionary[firstLevel].
ChildNodes[secondLevel].ConcatenatedMeaning;
if (warnCodesDictionary[firstLevel].ChildNodes[secondLevel].ChildNodes != null &&
warnCodesDictionary[firstLevel].ChildNodes[secondLevel].ChildNodes.ContainsKey(thirdLevel))
{
concatenatedMeaning =
warnCodesDictionary[firstLevel].
ChildNodes[secondLevel].
ChildNodes[thirdLevel].ConcatenatedMeaning;
if (warnCodesDictionary[firstLevel].ChildNodes[secondLevel].ChildNodes[thirdLevel].ChildNodes != null &&
warnCodesDictionary[firstLevel].ChildNodes[secondLevel].ChildNodes[thirdLevel].ChildNodes.ContainsKey(code))
{
concatenatedMeaning =
warnCodesDictionary[firstLevel].
ChildNodes[secondLevel].
ChildNodes[thirdLevel].
ChildNodes[code].ConcatenatedMeaning;
}
}
}
}
return concatenatedMeaning;
}
private static Dictionary<int, WarnCodeValue> GetChildren(XElement element, string concatenatedMeaning)
{
string elementMeaning = string.Empty;
XAttribute attribute = element.Attribute("meaning");
if (attribute != null)
{
elementMeaning = attribute.Value;
concatenatedMeaning =
string.IsNullOrEmpty(concatenatedMeaning) ? elementMeaning : string.Format("{0} : {1}", concatenatedMeaning, elementMeaning);
}
if (element.Descendants().Count() > 0)
{
Dictionary<int, WarnCodeValue> childNodeDictionary = new Dictionary<int, WarnCodeValue>();
foreach (XElement childElement in element.Elements())
{
int hex = Convert.ToInt32(childElement.Attribute("hex").Value, 16);
string meaning = childElement.Attribute("meaning").Value;
Dictionary<int, WarnCodeValue> dictionary = GetChildren(childElement, concatenatedMeaning);
WarnCodeValue warnCodeValue = new WarnCodeValue();
warnCodeValue.ChildNodes = dictionary;
warnCodeValue.Meaning = meaning;
warnCodeValue.ConcatenatedMeaning =
string.IsNullOrEmpty(concatenatedMeaning) ? meaning : string.Format("{0} : {1}", concatenatedMeaning, meaning);
childNodeDictionary.Add(hex, warnCodeValue);
}
return childNodeDictionary;
}
return null;
}
}
用法的
string concatenatedMeaning = WarnCodeProcessor.Instance.GetConcatenatedMeaning(0x2121);
输出
Current : Current, Device Input side : Short to Earth : Short to earth in Phase L1
可能的修改包括GetMeaning(code)
来检索代码的原始含义,而不是连接的含义。