如何在没有装箱的情况下比较System.Enum和枚举(实现)?

时间:2016-02-16 20:34:47

标签: c# enums

如何在没有装箱的情况下将System.Enumenum进行比较?例如,如何在不装箱enum

的情况下使以下代码正常工作
enum Color
{
    Red,
    Green,
    Blue
}

...

System.Enum myEnum = GetEnum(); // Returns a System.Enum. 
                                // May be a Color, may be some other enum type.

...

if (myEnum == Color.Red) // ERROR!
{
    DoSomething();
}

具体而言,这里的目的不是比较基础价值。在这种情况下,基础值并不重要。相反,如果两个枚举具有相同的基础值,如果它们是两种不同的枚举,则它们不应被视为相等:

enum Fruit
{
    Apple = 0,
    Banana = 1,
    Orange = 2
}

enum Vegetable
{
    Tomato = 0,
    Carrot = 1,
    Celery = 2
}

myEnum = Vegetable.Tomato;
if (myEnum != Fruit.Apple) // ERROR!
{
    // Code should reach this point 
    // even though they're the same underlying int values

    Log("Works!");
}

这基本上与Enum.Equals(Object)功能相同。不幸的是Equals()需要装箱枚举,在我们的例子中这将是一个顽皮的事情。

有没有一种很好的方法来比较两个没有装箱的任意枚举或者产生一堆开销?

感谢您的帮助!

7 个答案:

答案 0 :(得分:5)

这可以通过如下通用实现来完成:

private static bool Equals<TEnum>(Enum first, TEnum second)
    where TEnum : struct
{
    var asEnumType = first as TEnum?;
    return asEnumType != null && EqualityComparer<TEnum>.Default.Equals(asEnumType.Value, second);
}

唯一堆分配的内存将是每个EqualityComparer<TEnum>.Default值的延迟实例化,但是对于每种类型的枚举,这只会发生一次。

答案 1 :(得分:3)

除非你确实需要确保低延迟,否则我要小心担心GC达到这个级别。这并不意味着“我的网络服务器获得了大量的点击”,它意味着声音/视频播放,游戏或低延迟交易应用程序。如果它只是“我的应用程序必须不慢”,那么完全避免GC就是矫枉过正。如果你要避免装箱以避免一次分配,你需要避免所有其他分配,包括记录,创建闭包,任何字符串连接,当然还有任何使用new关键字作为参考类型。

但是,如果您真的处于必须避免GC的情况下,这是您的分配问题的最后或最差,那么可能值得研究。请务必阅读GC Latency Modes上的文档 - 如果您只需要特定时间段的低延迟,那么您可以根据需要更改GC的行为,而无需更改所有代码以避免分配内存。

所以看看枚举问题。正如其他人已经说过的那样,该值已经在System.Enum对象中加了框。你在评论中说这很好,因为这个值是为应用程序生命周期定义的一次。在这种情况下,我会考虑将其定义为枚举,而不是静态类值。有关一般方法的详细信息,请参阅this question

如果采用这种方法,那么为整个应用程序生命周期的每个可能的枚举值创建一个对象。然后你可以自由地使用参考比较或你想要的任何其他比较,你将不必处理拳击。

因此,对于您的示例,您可以使用类似

的内容
class Color
{
    public static Color Red { get; } = new Color();
    public static Color Green { get; } = new Color();

    private Color()
    {
    }
}

然后根据定义在两个对象之间进行枚举值之间的任何比较,因此在比较时不会发生装箱/取消装箱。现在你肯定明确区分不同的枚举类型 - Fruit.Apple和Vegetable.Tomato永远不会比较平等。

答案 2 :(得分:2)

您需要施放几乎不会影响性能的内容。 (即使你是拳击......你不会......它会产生很小的影响。)

如果您担心,请使用通用方法。

TEnum GetEnum<TEnum>() where TEnum : struct

然后你会得到你期待的背部类型,不会发生任何铸造或拳击。

答案 3 :(得分:2)

所以你有:

System.Enum myEnum = ...;

并且您知道myEnum已经是一些盒装枚举值。您希望将其与Color.Red进行比较而不创建其中包含Color.Red的其他框。你可以这样做:

if (myEnum is Color && (Color)myEnum == Color.Red)
{
  ...
}

is关键字将重复使用现有的框。从System.EnumColor的转换是一个拆箱转换(不确定类型检查是否会执行两次但我们真的在这里进行微优化)。 ==这里将对驻留在调用堆栈上的两个数值(例如,两个32位整数,取决于Color的基础整数类型)进行简单的整数比较。

答案 4 :(得分:1)

你无法检查

if (myEnum.GetType () != Fruit.Apple.GetType ()) ...

答案 5 :(得分:1)

您可以在if条件中查看Enum的类型:

if (myEnum is Fruit && myEnum.Equals(Fruit.Apple))
{
    // Code should reach this point 
    // even though they're the same underlying int values

    Log("Works!");
}

答案 6 :(得分:1)

您可以GetEnum返回object类型,而不是Enum类型,然后转到int

enum Fruit
{
    Apple = 0,
    Banana = 1,
    Orange = 2
}

enum Vegetable
{
    Tomato = 0,
    Carrot = 1,
    Celery = 2
}

object GetEnum()
{
    // for example
    return Fruit.Banana;
}

...

var myEnum = GetEnum();
Vegetable veg = Vegetable.Carrot;

if ((int)myEnum == (int)veg)
{
    Console.Write("SAME");
}

不确定这需要多少开销。