简体   繁体   English

无论C#中的基础类型如何,都会对System.Enum值进行常规处理

[英]General handling of System.Enum values regardless of underlying type in C#

I implemented a method that takes two Enum values as parameters: 我实现了一个方法,它将两个枚举值作为参数:

void HandleEnumValue( System.Enum v, System.Enum bits )

The actual enum-type is not necessarily the same for the two parameters.. 两个参数的实际枚举类型不一定相同。

In this method I assumed that the underlying type of enum is int. 在这个方法中,我假设枚举的基础类型是int。 Now for the first time this method is called with enum values that have long as the underlying type, and the method now throws an exception. 现在,第一次使用枚举值调用此方法,该值作为基础类型,并且该方法现在抛出异常。

I like to enhance the method so that it can take any enum-value , regardless of the underlying type. 我喜欢增强方法,以便它可以采用任何枚举值 ,无论底层类型如何。 I have an implementation for that, but I don't like it very much. 我有一个实现,但我不太喜欢它。

I wonder if there is a more general/maintainable solution? 我想知道是否有更通用/可维护的解决方案?

Alas, I cannot use a generic type parameter, because this would introduce a breaking change in the library, and the zillions of calls to my method would have to be replaced. 唉,我不能使用泛型类型参数,因为这会在库中引入重大变化,并且必须替换对我的方法的数以万计的调用。

Here's the code, and my not-so-good-solution: 这是代码,我不太好的解决方案:

using System;

[Flags()] public enum ByteEnum : byte { One = 1, SomeByte  = 0x42 } 
[Flags()] public enum ShortEnum: short{ One = 1, SomeShort = 0x4223 } 
[Flags()] public enum IntEnum  : int  { One = 1, SomeInt   = 0x42230815 } 
[Flags()] public enum LongEnum : long { One = 1, SomeLong  = 0x4223081547112012L } 

public class Program 
{
    public static void Main()
    {
        HandleEnumValue( ByteEnum.SomeByte,   ByteEnum.One  ) ;
        HandleEnumValue( ShortEnum.SomeShort, ShortEnum.One ) ;
        HandleEnumValue( IntEnum.SomeInt,     IntEnum.One   ) ;
        HandleEnumValue( LongEnum.SomeLong,   LongEnum.One  ) ;

        // will throw InvalidCastException: HandleEnumValueOriginal( ByteEnum.SomeByte,   ByteEnum.One  ) ;
        // will throw InvalidCastException: HandleEnumValueOriginal( ShortEnum.SomeShort, ShortEnum.One ) ;
        HandleEnumValueOriginal( IntEnum.SomeInt,     IntEnum.One   ) ;
        // will throw InvalidCastException: HandleEnumValueOriginal( LongEnum.SomeLong,   LongEnum.One  ) ;
    }

    // new implementation, that I dislike.
    static void HandleEnumValue( System.Enum v, System.Enum bits )
    {
        // assert underlying types of v and bits are equal
        if ( v.GetTypeCode() != bits.GetTypeCode() ) throw new Exception( "enum type code mismatch" ) ;

        bool hasBitsSet = false ;   // won't compile: bool hasBitsSet = ( ( v & bits ) == bits ) ;
        long lvalue     = 0L ;      // will throw   : lvalue = (long)(object)v

        switch ( v.GetTypeCode() )
        {
            case TypeCode.Byte : 
                hasBitsSet = ( ( (byte)(object)v &  (byte)(object)bits ) ==  (byte)(object)bits ) ;
                lvalue = (long)(byte)(object)v ;
                break ;
            case TypeCode.Int16 : 
                hasBitsSet = ( ( (short)(object)v &  (short)(object)bits ) ==  (short)(object)bits ) ;
                lvalue = (long)(short)(object)v ;
                break ;
            case TypeCode.Int32 : 
                hasBitsSet = ( ( (int)(object)v &  (int)(object)bits ) ==  (int)(object)bits ) ;
                lvalue = (long)(int)(object)v ;
                break ;
            case TypeCode.Int64 : 
                hasBitsSet = ( ( (long)(object)v &  (long)(object)bits ) ==  (long)(object)bits ) ;
                lvalue = (long)(object)v ;
                break ;
        }

        Console.WriteLine( "(new) enum value   = " + v.ToString() ) ;
        Console.WriteLine( "(new) number value = " + lvalue.ToString() ) ;
        Console.WriteLine( "(new) has bits set = " + hasBitsSet.ToString() ) ;

        // further processing ...
    }

    // original implementation, that doesn't work anymore
    static void HandleEnumValueOriginal( System.Enum v, System.Enum bits )
    {
        int  lvalue     = (int)(object)v ;
        bool hasBitsSet = ( ( (int)(object)v & (int)(object)bits ) == (int)(object)bits ) ;
        Console.WriteLine( "(original) enum value   = " + v.ToString() ) ;
        Console.WriteLine( "(original) number value = " + lvalue.ToString() ) ;
        Console.WriteLine( "(original) has bits set = " + hasBitsSet.ToString() ) ;
        // further processing ...
    }

}

In .NET 4, there is an added method that does almost what you want: Enum.HasFlag . 在.NET 4中,有一个添加的方法几乎可以满足您的需求: Enum.HasFlag

Unfortunately, this method throws if the actual enum types are different. 不幸的是,如果实际的枚举类型不同,则抛出此方法。 Its implementation is quite simple to replicate without enum type checking or if you're using an older version of the framework: 它的实现非常简单,无需枚举类型检查即可复制,或者如果您使用的是旧版本的框架:

static void HandleEnumValue(Enum v, Enum bits) {
    ulong enumValue = ToUInt64(v);
    ulong bitsValue = ToUInt64(bits);
    bool hasBitsSet = (enumValue & bitsValue) == bitsValue;
    Console.WriteLine("enum value   = " + v);
    Console.WriteLine("number value = " + bits);
    Console.WriteLine("has bits set = " + hasBitsSet);
}

internal static ulong ToUInt64(Enum value) {
    switch (Convert.GetTypeCode(value)) {
        case TypeCode.SByte:
        case TypeCode.Int16:
        case TypeCode.Int32:
        case TypeCode.Int64:
            return (ulong) Convert.ToInt64(value);
        case TypeCode.Byte:
        case TypeCode.UInt16:
        case TypeCode.UInt32:
        case TypeCode.UInt64:
            return Convert.ToUInt64(value);
        default:
            throw new ArgumentOutOfRangeException("value");
    }
}

This method will handle any type of enum and any underlying type, even if they're different. 此方法将处理任何类型的枚举和任何基础类型,即使它们不同。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM