如何避免将枚举类型与整数进行比较?

5

我正在使用Enum类型,目的是为了防止使用虚假值:

Public Class MyClass1

    Public Enum MyEnum As Byte
        FIRST
        SECOND
    End Enum

    Private my_var As MyEnum

    Public Property MyVar As MyEnum
        Get
            Return my_var
        End Get
        Set
            my_var = Value
        End Set
    End Property

End Class

Public Sub MyWorks

    Dim my_object As MyClass1 = New MyClass1

    my_object.MyVar = 1      ' Compilation Error
    my_object.MyVar = 33     ' Compilation Error

    If my_object.MyVar = 1 Then       ' No Compilation Error
    End If
    If my_object.MyVar = 27 Then      ' No Compilation Error
    End If
    If my_object.MyVar = 3.141 Then   ' No Compilation Error
    End If
    If my_object.MyVar = "Fred" Then  ' Compilation Error
    End If

End Sub

这个是用Option Strict On; Option Explicit On编译的。

正如我所预料的那样,尝试对枚举属性进行赋值会产生编译错误(Option Strict On不允许从“Integer”到“MyClass.MyEnum”的隐式转换)。

但前三个比较并没有产生编译错误,我原本希望它们会出错(特别是第二个和第三个,它们很无意义)。第四个比较无法编译,但错误信息似乎很奇怪:

Option Strict On不允许从“String”到“Double”的隐式转换

有人知道如何强制使所有这些比较都出现编译错误吗?


我不这么认为,伙计。发生的是一种扩展转换。Byte数据类型扩展到Byte, Short, UShort, Integer, UInteger, Long, ULong, Decimal, Single, Double。第四个比较的错误信息非常奇怪。MyVar不是一个double。此代码片段中的Option Explicit也是不必要的。 - scottyeatscode
1个回答

4
    my_object.MyVar = 1 ' Compilation Error
原因
my_object.MyVar 是类型为 MyEnum 的变量,而 1 是整型(Byte/UShort/Etc.)类型。因此,在使用 Option Strict On 时会出现编译错误。请改用以下方式:
    my_object.MyVar = MyEnum.SECOND
    ' .SECUND should mean "1" as FIRST would be "0" by default..

但是为什么呢?"1"应该是Byte类型,因为您已经明确地“强制类型转换”了枚举..! 好吧,一旦打开Option Strict,您就不能再将文字整数(Byte)值分配给枚举。如果关闭Option Strict,它可以工作! 但是您可能会想知道为什么在打开Option Strict的情况下,以下内容也可以正常工作:

    Dim MyByteVar As Byte = 1 ' No compilation error

MyByteVar的类型是Byte,文字值后面没有任何类型字符标识符,因此文字值"1"被假定为Integer类型。但由于编译器知道MyByteVar的类型是Byte,它试图将"1"转换为Byte,并且转换成功了。没有出现编译错误。
因此,不要将缩小转换显式类型不匹配混淆。使用Option Strict On将"1"转换为MyEnum类型将不起作用,这不是因为编译器无法将1转换为枚举中匹配的值,而是因为编译器不应该尝试这样做。明确声明强类型和严格类型分配的主要目的是避免MSDN Option Strict页面所述的风险。此外,在 MSDN枚举页面上,您将看到类似的说明:

WRONG 对不起,我被上周遇到的另一个问题所愚弄了。你不能使用Option Explicit On进行缩小转换(Integer -> Byte)。
编辑 2:^^ 看来我还是对的 :/ 嗯,说实话我不确定,对不起对不起..

  • 减少由数字错位或输错引起的错误。
  • 使未来更改值变得容易。
  • 使代码更易于阅读,这意味着更不容易引入错误。
  • 确保向前兼容性。如果您使用枚举,则将来有人更改与成员名称对应的值时,您的代码可能会更不容易出错。

如果你可以接受 Dim my_var As MyEnum = 1 这样的赋值方式,那么就没有理由创建 Enumerations, Option StrictOption Explicit。这些安全检查存在的目的就是让你的代码/编程更加安全,同时缩小了任意编写代码的范围。

    If my_object.MyVar = 1 Then
    ' my_object.MyVar = MyEnum.FIRST = 0 -> Byte (strongly typed)
    ' 1 -> Integer by default
    ' Convert my_object.MyVar to Integer (always a widening conversion)
    ' 0 is different from 1 (Integer to Integer comparison)
    ' -> FALSE - No compilation error


    If my_object.MyVar = 27 Then
    ' Same as above and will return FALSE

    If my_object.MyVar = 3.141
    ' my_object.MyVar = MyEnum.FIRST = 0 -> Byte (strongly typed)
    ' 3.141 -> will default to Double (because you didn't used Type Character flag)
    ' Convert my_object.MyVar to Double (always a widening conversion)
    ' 0 is different from 3.141 (Double to Double comparison)
    ' -> FALSE - No compilation error

如果您将my_object.MyVar的值设置为MyEnum.SECOND,以下内容不会产生编译错误,并且将与TRUE进行比较:
    If my_object.MyVar = 1
    ' my_object.MyVar = MyEnum.SECUND = 1 -> Byte (strongly typed)
    ' 1 -> will default to Integer
    ' Convert my_object.MyVar to Integer = 1
    ' 1 = 1 => TRUE !

以下内容与字节赋值类似:
    If my_object.MyVar = "Fred" Then '...

使用Option Strict On,不允许将Double转换为String。这是明显的类型不匹配,而Option Strict禁止了这种情况。但为什么不是Byte?因为编译器在尝试获取类型匹配时会尝试一次又一次的扩展。Byte -> Integer -> Long -> .. -> Double。
你应该将my_object.MyVar明确转换为String,或者将“Fred”明确转换为数字值。比较测试将始终尝试处理所需的扩展转换(如果可能),但只有基本的扩展转换被Option Strict On允许。

那么如何使您的代码在前三行比较时在编译时失败呢?嗯,我不知道。也许值得质疑 Option Strict 允许什么,不允许什么,所以我认为这更是一个哲学问题而不是实际问题。

=> 一个返回布尔值的表达式/评估是否应禁止不同类型数字之间的比较?
=> 当 Option Strict 开启时,是否应禁止基本扩展转换 Byte -> Double?

很抱歉,我没有资格回答这样的问题...


1
我也不太有资格回答哲学问题,但这从未阻止过我... 我认为将“枚举”转换为整数不应该是一种扩展转换。我刚刚发现我的一些代码中存在缺陷,例如 Dim x As EnumOne,然后稍后出现了 If x = EnumTwo.Value Then ...。我在比较中意外使用了错误的“枚举”。编译器愉快地将两侧扩展为整数,并没有报告警告。我想要一个编译错误!这就是为什么我选择 Option Strict On 的原因。 - MarkJ

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