如何将枚举标志与字典键进行比较?

3

我有这个枚举标志:

[Flags()]
public enum Levels
{
    Beginner, Medium, Advanced, Master
}

我有一个名为“Bank”的属性,其中包含一个Dictionary<Levels, ...>,而Levels是您可以选择的可能选项。
假设我的第一个KeyValuePair包含以下内容:Key = Levels.Beginner | Levels.Medium | Levels. Advanced。所以,如果我在字典中输入Levels.Medium,它将返回最后一个对象,因为Medium是一种可能的值。
public Worksheet LoadWorksheet(Levels level)
{
    Worksheet worksheet = new Worksheet(this.Bank[level].Value, this.Bank[level].Key);
    return worksheet;
}

但是不幸的是,当我这样做时,会出现错误指出该键不存在。我应该怎么做才能匹配该键呢?

2个回答

2
首先,我应该指出,如果:
Levels keyA = Levels.Beginner | Levels.Medium | Levels. Advanced;
Levels keyB = Levels.Medium;

接下来:

Debug.Assert(keyA.GetHashCode() != keyB.GetHashCode());
Debug.Assert(keyA != keyB);

在查找字典中的值时,字典首先使用键的哈希值来确定正确的桶,然后使用相等比较来识别桶中的正确键。
如果哈希值不相等,则无法找到该键。如果键值不相等,则无法找到该值。
您可以通过以下LINQ表达式查看所有具有包含“Levels.Medium”的键的条目,以查看其位模式是否存在于键中:
var mediumEntries = Bank.Where(entry => 0 != ((int)entry.Key & (int)Levels.Medium));

或者,正如 @Ria 指出的那样,在 .Net 4 中您可以使用 HasFlags 成员:

var mediumEntries = Bank.Where(entry => entry.Key.HasFlag(Levels.Medium));

另外的答案中提到了一个好点子(@dasblinkenlight, @Ria),即您枚举的值需要具有非重叠的位模式才能使此方法起作用:
[Flags()]        
public enum Levels        
{        
    Beginner = 0x01, 
    Medium = 0x02, 
    Advanced = 0x04, 
    Master = 0x08
}   

我明白,但在这种情况下,我该如何处理这个问题?在这种情况下,keyB包含A内的一个值,所以我正在尝试匹配它...但我不知道该怎么做。 - Darf Zon
@DarfZon 我正在尝试创建一个示例。KeyValuePair的Value类型是什么? - Monroe Thomas
这是一个 List<T>,其中 T 是自定义类。 - Darf Zon
我只是想补充一下,由于您不再直接按键查找值,而是使用LINQ表达式在Dictionary上搜索,就好像它是一个IEnumerable<KeyValuePair>,因此您基本上错过了大部分Dictionary的性能提升。您没有使用内部哈希表的快速查找功能。 - Avner Shahar-Kashtan

0

在定义枚举常量时,应该使用2的幂次方,例如1、2、4、8等。这样可以确保组合枚举常量中的各个标志不会重叠:

[Flags]
public enum Levels
{
    Beginner = 1, 
    Medium = 2,
    Advanced = 4, 
    Master = 8
}

你必须将字典键定义为整数:Dictionary<int, ...>。并在添加到字典时强制转换为int

Bank.Add((int) (Levels.Medium|Levels.Master), ...);

然后将 Key 与枚举标志进行比较:

if ((this.Bank[level].Key & Levels.Advanced) == Levels.Advanced)
{
     // Do something
}

如果您使用的是.NET4,请使用HasFlag

if ( this.Bank[level].Key.HasFlag(Levels.Advanced) )
{
     // Do something
}

注意:

Dictionary.Key 必须是唯一的。否则在尝试添加重复键时会抛出 ArgumentException 异常。因此不建议将标志枚举用作 Dictionary.Key。请尝试将其存储在其他位置。


为什么键类型必须是 int?这是否意味着操作者必须始终将键值强制转换回枚举类型?我认为,在执行按位操作时,将枚举值转换为 int 更好。 - Monroe Thomas

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接