比较两个类型为T的System.Enum

17

我刚刚发现System.Enum不易实现为通用类型。当比较两个T类型的枚举时,编译器会抛出错误:

if(button.Identifier == Identifier) // (in AbstractInputDevice)

我认为我不能比较这两个枚举类型,因为它们实际上并不被认为是枚举类型。因此没有可用的比较方法。如何将它们进行相等比较?
以下是更多细节:
public class Button<TEnum> where TEnum : struct, IConvertible, IComparable, IFormattable {
   public TEnum Identifier {
        get;
        private set; //Set in the ctor
    }
}

并且

public abstract class AbstractInputDevice<TEnum> where TEnum : struct, IConvertible, IComparable, IFormattable {

   private List<Button<TEnum>> _buttons = new List<Button<TEnum>>();

   public Button<TEnum> GetButton(TEnum Identifier){
        foreach(Button<TEnum> button in _buttons){
            if(button.Identifier == Identifier) //<- compiler throws
                return button;
        }
        Debug.Log("'" + GetType().Name + "' cannot return an <b>unregistered</b> '" + typeof(Button<TEnum>).Name + "' that listens to '" + typeof(TEnum).Name + "." + Identifier.ToString() + "'.");
        return null;
    }
}

一个InputDevice可能看起来像这样:
public class Keyboard : AbstractInputDevice<KeyCode> {
    private void Useless(){
        Button<KeyCode> = GetButton(KeyCode.A);
    }
}

我用了这个资源:
创建泛型方法并将T约束为枚举类型

2
编译器具体报了什么错误? - Ron Beyer
1
你可以尝试使用 button.Identifier.Equals(Identifier),为了让代码更简洁:var button = _buttons.Where(b => b.Identifier.Equals(Identifier)).FirstOrDefault(); - Alex
@RonBeyer 我使用Unity3D,他们的编译器有时会让人感到有些困惑。现在它显示“未预期的符号'=='”,就好像有一个错别字一样。但在做出某些更改之前,它显示了不同的内容,例如“'=='不能应用于类型为'TEnum和'TEnum'的操作数”。 - Noel Widmer
我会说你需要制作一个 MCVE - Blorgbeard
@Alex 你是认真的吗?再也没有错误了! - Noel Widmer
2个回答

26

不是不可能的事情

button.Identifier == Identifier

你应该使用

EqualityComparer<TEnum>.Default.Equals(button.Identifier, Identifier)

这样可以避免将值装箱为一个object盒子(或IComparable盒子)。


那将是更好的比较,同意。 - Alex
1
有趣的是,EqualityComparer(of enumType).Default 避免了装箱,考虑到枚举类型本身并没有实现 IEquatable。我想知道 EqualityComparer(of T).Default 除了 IEquatable 还寻找什么? - supercat
@JeppeStigNielsen:在我的当前.NET中,它不会进行装箱操作,这让我很好奇是否有其他类型的值类型适用于EqualityComparer<T>.Default的优化,以及是否自2.0以来出现了任何此类行为变化;编写一个“创建默认相等比较器”的方法肯定是可能的,该方法将分析要比较的内容并生成代码来执行适当的比较,但我不知道.NET是否这样做。 - supercat
4
仅查看.Default实例的运行时类型就可以得到一些线索。对于枚举类型E,我得到了System.Collections.Generic.EnumEqualityComparer`1[E],因此存在专门支持枚举的特殊支持。鉴于此,他们肯定会注意并避免装箱。对于一个实现了IEquatable`1[G]的结构体G,我得到了 System.Collections.Generic.GenericEqualityComparer`1[G]。最后,对于一个没有泛型等式支持的结构体N,我得到了System.Collections.Generic.ObjectEqualityComparer`1[N]补充:适用于.NET 4.5.2。 - Jeppe Stig Nielsen
通过这样的优化,性能得到了显著提升,但为了达到最佳效果,最好为实现了 IEquatable<T> 的结构体定义一个属性,以便在包含在其他结构体中时,外部结构体也可以进行位比较。 - supercat
显示剩余4条评论

0

您正在尝试对值类型(结构体)执行引用比较,请改用Equals进行相等性比较:

public Button<TEnum> GetButton(TEnum Identifier) {
    var button = _buttons
        .Where(b => EqualityComparer<TEnum>.Default.Equals(b.Identifier, Identifier))
        .FirstOrDefault();

    if (button == null)
        Debug.Log("'" + GetType().Name + "' cannot return an <b>unregistered</b> '" + typeof(Button<TEnum>).Name + "' that listens to '" + typeof(TEnum).Name + "." + Identifier.ToString() + "'.");
    return button;
}

button.Identifier == Identifier 语句无法执行,因为结构体中不存在 == 运算符。如果是类,则会执行引用比较。

正如 @JeppeStigNielsen 在他的 答案 中所指出的,为了避免装箱等值比较,最好使用 EqualityComparer<TEnum>.Default.Equals 方法。


".Equals()在评论中所述的功能正常。但是我不知道该如何将您发布的这行新代码实现到我的if语句中..." - Noel Widmer
@NoelWidmer 在您原始的 GetButton 方法的上下文中添加了它。 - Alex
List<T>.Where是一个扩展方法吗?如果是,你知道它在哪个命名空间中吗?(它没有被识别为_buttons的成员) - Noel Widmer
@NoelWidmer 是的,你需要添加一个 using System.Linq; 语句。 - Alex
确实 - 我是的。非常感谢! - Noel Widmer

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