代码之家  ›  专栏  ›  技术社区  ›  CesarGon

在C中将int强制转换为enum#

  •  35
  • CesarGon  · 技术社区  · 14 年前

    在C#中有一些我无法理解的东西。你可以在射程之外施放一颗子弹 int enum 编译器也不会退缩。想象一下 枚举

    enum Colour
    {
        Red = 1,
        Green = 2,
        Blue = 3
    }
    

    现在,如果你写:

    Colour eco;
    eco = (Colour)17;
    

    编译器认为这很好。还有运行时。UH

    为什么C#团队决定让这成为可能?我认为,这一决定忽略了在以下情况下使用枚举的意义:

    void DoSomethingWithColour(Colour eco)
    {
        //do something to eco.
    }
    

    在C#这样的强类型语言中,我想假设 eco Colour 生态的 生态的 持有法律许可证 颜色 价值我需要显式地测试它,并按照我的意愿处理异常值。为什么会这样?

    以我的拙见,如果编译器在抛出超出范围的代码时发出错误(甚至警告)消息会更好 枚举 值在编译时是已知的。如果不是,运行时应该在赋值语句中抛出异常。

    你怎么认为?这有什么原因吗?

    I posted ages ago on my blog

    12 回复  |  直到 12 年前
        1
  •  31
  •   Henk Holterman    11 年前

    猜测“为什么”总是危险的,但请考虑一下:

    enum Direction { North =1, East = 2, South = 4, West = 8 }
    Direction ne = Direction.North | Direction.East;
    
    int value = (int) ne; // value == 3
    string text = ne.ToString();  // text == "3"
    

    [Flags] 属性放在枚举的前面,最后一行更改为

    string text = ne.ToString();  // text == "North, East"
    
        2
  •  15
  •   johnny g    14 年前

    // a simple enum
    public enum TransmissionStatus
    {
        Success = 0,
        Failure = 1,
        Error = 2,
    }
    // a consumer of enum
    public class MyClass 
    {
        public void ProcessTransmissionStatus (TransmissionStatus status)
        {
            ...
            // an exhaustive switch statement, but only if
            // enum remains the same
            switch (status)
            {
                case TransmissionStatus.Success: ... break;
                case TransmissionStatus.Failure: ... break;
                case TransmissionStatus.Error: ... break;
                // should never be called, unless enum is 
                // extended - which is entirely possible!
                // remember, code defensively! future proof!
                default:
                    throw new NotSupportedException ();
                    break;
            }
            ...
        }
    }
    

    问题是,我如何测试最后一个case子句?假设有人会延长期限是完全合理的 TransmissionStatus MyClass 在上面然而,我仍然想验证它在这种情况下的行为。一种方法是使用铸造,例如

    [Test]
    [ExpectedException (typeof (NotSupportedException))]
    public void Test_ProcessTransmissionStatus_ExtendedEnum ()
    {
        MyClass myClass = new MyClass ();
        myClass.ProcessTransmissionStatus ((TransmissionStatus)(10));
    }
    
        3
  •  5
  •   Mike Chamberlain    14 年前

    我当然明白塞萨尔的观点,我记得这一点最初也让我感到困惑。在我看来,enum在当前的实现中确实有点太低了,并且存在漏洞。在我看来,这个问题有两种解决办法。

    1) 如果枚举的定义具有FlagsAttribute,则仅允许在枚举中存储任意值。这样,我们可以在适当的时候(并显式声明)继续将它们用于位掩码,但当仅用作常量的占位符时,我们将在运行时检查该值。

    2) 引入一个单独的原语类型,称为bitmask,它允许任何ulong值。同样,我们将标准枚举限制为仅声明的值。这将有额外的好处,允许编译器为您分配位值。因此:

    [Flags]
    enum MyBitmask
    { 
        FirstValue = 1, SecondValue = 2, ThirdValue = 4, FourthValue = 8 
    }
    

    bitmask MyBitmask
    {
        FirstValue, SecondValue, ThirdValue, FourthValue 
    }
    

    毕竟,任何位掩码的值都是完全可预测的,对吗?作为一名程序员,我非常高兴能把这些细节抽象出来。

    不过,现在已经太迟了,我想我们永远都要坚持当前的实现/

        4
  •  4
  •   Wim    14 年前

    您不需要处理异常。该方法的先决条件是调用方应该使用枚举,而不是将任何任意nilly int强制转换到所述枚举。那太疯狂了。枚举不是重点吗

    就我而言,任何一个向颜色枚举投17分的开发者都需要17分的支持。

        5
  •  4
  •   Bryan Rowe    14 年前

    这就是为什么永远不应该为枚举分配整数值的原因之一。如果它们具有需要在代码的其他部分中使用的重要值,请将枚举转换为对象。

        6
  •  4
  •   Jaider    12 年前

    这是出乎意料的。。。我们真正想要的是控制演员阵容。。。例如:

    Colour eco;
    if(Enum.TryParse("17", out eco)) //Parse successfully??
    {
      var isValid = Enum.GetValues(typeof (Colour)).Cast<Colour>().Contains(eco);
      if(isValid)
      {
         //It is really a valid Enum Colour. Here is safe!
      }
    }
    
        7
  •  3
  •   Otávio Décio    14 年前

    这类似于声明一个只接受1到100之间的值的整数;我见过的唯一支持这种级别检查的语言是CHILL。

        8
  •  3
  •   Jon Schneider Stefan    7 年前
    if (!Enum.IsDefined(typeof(Colour), 17))
    {
        // Do something
    }
    
        9
  •  1
  •   Broam    14 年前

    简短版本:

    尝试将枚举更改为仅允许有效值(可能是默认回退)的整数需要帮助器方法。在这一点上,您没有枚举——实际上您有一个类。

    如果INT是重要的——就像布莱恩·罗(Bryan Rowe)所说的那样,情况就更糟了。

        10
  •  0
  •   Mike Chamberlain    13 年前

    我想我会分享我最后用来验证枚举的代码,因为到目前为止,我们这里似乎没有任何有效的东西。。。

    public static class EnumHelper<T>
    {
        public static bool IsValidValue(int value)
        {
            try
            {
                Parse(value.ToString());
            }
            catch
            {
                return false;
            }
    
            return true;
        }
    
        public static T Parse(string value)
        {
            var values = GetValues();
            int valueAsInt;
            var isInteger = Int32.TryParse(value, out valueAsInt);
            if(!values.Select(v => v.ToString()).Contains(value)
                && (!isInteger || !values.Select(v => Convert.ToInt32(v)).Contains(valueAsInt)))
            {
                throw new ArgumentException("Value '" + value + "' is not a valid value for " + typeof(T));
            }
    
            return (T)Enum.Parse(typeof(T), value);
        }
    
        public static bool TryParse(string value, out T p)
        {
            try
            {
                p = Parse(value);
                return true;
            }
            catch (Exception)
            {
                p = default(T);
                return false;
            }
    
        }
    
        public static IEnumerable<T> GetValues()
        {
            return Enum.GetValues(typeof (T)).Cast<T>();
        }
    }
    
        11
  •  0
  •   sliderhouserules    9 年前

    这是一个老问题,但最近我很困惑,谷歌把我带到了这里。我找到了一篇经过进一步搜索的文章,这篇文章最终让我点击了一下,我想我会回来和大家分享的。

    要点是枚举是一个结构,这意味着它是一个值类型( source

    说到这里,这里是我编写的一个扩展方法的核心,它可以轻松地将事物转换/解析为枚举:

    if (value != null)
    {
        TEnum result;
        if (Enum.TryParse(value.ToString(), true, out result))
        {
            // since an out-of-range int can be cast to TEnum, double-check that result is valid
            if (Enum.IsDefined(typeof(TEnum), result.ToString()))
            {
                return result;
            }
        }
    }
    
    // deal with null and defaults...
    

    变量 value TryParse IsDefined 按顺序 ,我可以解析刚才提到的所有类型,包括大小写混合的字符串,它非常健壮。

    用法如下(假定为NUnit):

    [Test]
    public void MultipleInputTypeSample()
    {
        int source;
        SampleEnum result;
    
        // valid int value
        source = 0;
        result = source.ParseToEnum<SampleEnum>();
        Assert.That(result, Is.EqualTo(SampleEnum.Value1));
    
        // out of range int value
        source = 15;
        Assert.Throws<ArgumentException>(() => source.ParseToEnum<SampleEnum>());
    
        // out of range int with default provided
        source = 30;
        result = source.ParseToEnum<SampleEnum>(SampleEnum.Value2);
        Assert.That(result, Is.EqualTo(SampleEnum.Value2));
    }
    
    private enum SampleEnum
    {
        Value1,
        Value2
    }
    

    免责声明:

        12
  •  -1
  •   CmdrTallen    14 年前

    它将使用Enum.Parse()抛出错误;

    Enum parsedColour = (Colour)Enum.Parse(typeof(Colour), "17");
    

    如果您愿意,可能值得用来引发运行时错误。