在C#中将内容转换为枚举

Ces*_*Gon 35 c# enums casting

在C#中有一些我无法理解的东西.您可以将超出范围int转换为a enum,编译器不会退缩.想象一下enum:

enum Colour
{
    Red = 1,
    Green = 2,
    Blue = 3
}
Run Code Online (Sandbox Code Playgroud)

现在,如果你写:

Colour eco;
eco = (Colour)17;
Run Code Online (Sandbox Code Playgroud)

编译器认为没问题.还有运行时.呃?

为什么C#团队决定实现这一目标?我认为,在这样的场景中,这个决定忽略了使用枚举的重点:

void DoSomethingWithColour(Colour eco)
{
    //do something to eco.
}
Run Code Online (Sandbox Code Playgroud)

在像C#这样的强类型语言中,我想假设它eco总是具有合法Colour价值.但这种情况并非如此.程序员可以调用我的方法,其值为17 eco(如前面的代码片段所示),因此我的方法中的代码不能假定它eco具有合法Colour值.我需要明确地测试它并按照我的要求处理特殊值.为什么是这样?

在我的拙见中,如果编译器在将超出范围int转换为a时发出错误(甚至是警告)消息enum,如果int在编译时已知该值,则会更好.如果没有,运行时应该在赋值语句中抛出异常.

你怎么看?这有什么原因吗?

(注意:这是我在很久以前在我的博客上发布的一个问题,但没有得到任何信息回复.)

Hen*_*man 31

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

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"
Run Code Online (Sandbox Code Playgroud)

[Flags]属性放在枚举前面时,最后一行会变为

string text = ne.ToString();  // text == "North, East"
Run Code Online (Sandbox Code Playgroud)

  • 不,不同意.位字段是对枚举的完全有效使用,以至于甚至不需要[Flags]属性来使用它们. (20认同)
  • @CesarGon ......不.如果我想在属性上设置标志,我不想列出每个可能的选项组合.对于方向,这可能是微不足道的,但在大多数其他情况下,它会很糟糕. (5认同)
  • 是的,标志枚举是一个有效的设计,没有问题.但我担心结果是枚举类型提供的价值(在类型安全方面)比它们的价值低.也许语言设计者应该将枚举类型视为一个事物而将位域(在C#中称为标记枚举)作为一种不同的东西,在语言中使用不同的结构.我想拥有enum类型应该给你的类型安全性,以及bitfields应该提供的灵活性. (5认同)
  • 它们被称为标志枚举.非常有趣的设计. (2认同)
  • @CesarGon:想象一下你的标志枚举不包含4位而是16位.你真的想定义所有65536种可能的组合(假设所有组合都有效)吗?[flags]属性的要点就是您不必这样做.(但是你的积分仍然有效:你仍然可以施放(方向)16,尽管16不是有效的方向) (2认同)

joh*_*y g 15

不知道为什么,但我最近发现这个"功能"非常有用.前几天我写了这样的东西

// 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;
        }
        ...
    }
}
Run Code Online (Sandbox Code Playgroud)

问题是,我该如何测试最后一个案例条款?假设有人可能延长TransmissionStatus而不更新其消费者是完全合理的,就像MyClass上面的穷人一样.但是,我还是想在这种情况下验证它的行为.一种方法是使用铸造,例如

[Test]
[ExpectedException (typeof (NotSupportedException))]
public void Test_ProcessTransmissionStatus_ExtendedEnum ()
{
    MyClass myClass = new MyClass ();
    myClass.ProcessTransmissionStatus ((TransmissionStatus)(10));
}
Run Code Online (Sandbox Code Playgroud)

  • 所以...假设枚举是完全类型安全的值验证,你将如何测试前面的条件?您是否愿意在为了测试默认条款的明确目的而编写的每个枚举中添加"不支持"值?如果你问我,那闻起来就像一个更大的臭味;) (4认同)

Mik*_*ain 5

我当然看到了Cesar的观点,我记得这也让我很困惑.在我看来,枚举,在他们目前的实施中,确实有点太低水平和漏洞.在我看来,这个问题会有两种解决方案.

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

2)引入一个名为say,bitmask的独立原始类型,它允许任何ulong值.同样,我们仅将标准枚举限制为声明值.这样可以让编译器为您分配位值.所以这:

[Flags]
enum MyBitmask
{ 
    FirstValue = 1, SecondValue = 2, ThirdValue = 4, FourthValue = 8 
}
Run Code Online (Sandbox Code Playgroud)

等同于:

bitmask MyBitmask
{
    FirstValue, SecondValue, ThirdValue, FourthValue 
}
Run Code Online (Sandbox Code Playgroud)

毕竟,任何位掩码的值都是完全可预测的,对吧?作为一名程序员,我非常乐意将这些细节抽象出去.

现在还为时已晚,我想我们永远都会坚持当前的实施.:/