I keep reading the statement that 'Enumeration is a datatype'(for example see here ). I am trying to understand the meaning of the statement better. So I wrote the following code.
Imports System
Imports System.Enum
Module Program
Enum colours As Byte
Red = 5
Blue
Green
End Enum
Sub Main(args As String())
Dim c As colours
Console.WriteLine(c.Red) 'Prints 5
Console.ReadLine()
End Sub
End Module
Because the enum has been declared to be of type BYTE it means that the type of the constants associated with the fields of the enum would be of type byte. But this does not really explain the idea of enum as a data type. So i tried defining a variable of the type colours
and tried accessing the enumeration members through Dim c As colours
and c.Red
and it worked. However two questions comes at this point:
c.Red
gives a warning messageAccess of a shared member, enum member or nested type through an instance; qualifying expression will not be evaluated
. I understand the first sentence in the warning message but not the second sentence. What would be the qualifying expression in this case which shall not be evaluated --c.Red
works fine??Although I can access enum mmebers through the variable
c
, it seems unnecessary because the same thing can be done by writingcolours.red
. So although the statementDim c as colours
makes the enum look more as a data type it does not seem to be of much use. If that is right, can someone pls give me an example which illustrates in a more realistic manner the idea of enum as a data type.
CodePudding user response:
Your 2.
sort of answers your 1.
You are not supposed to do c.Red
, you are supposed to do colours.Red
, and if you do c.Red
you get the warning.
The qualifying expression that will not be evaluated is c
.
That is, the compiler will not generate code that accesses the variable c
. Evaluating a variable is a trivial read-only action that you may even not consider "evaluation", but if you had e.g.
Sub Main()
Dim c As colours = SomeFunction(AnotherFunction(), YetAnotherFunction()).Red
End Sub
Function SomeFunction(ByVal a As Integer, ByVal b As String) As colours
Console.WriteLine("SomeFunction called")
Return colours.Blue
End Function
Function AnotherFunction() As Integer
Console.WriteLine("AnotherFunction called")
Return 42
End Function
Function YetAnotherFunction() As String
Console.WriteLine("YetAnotherFunction called")
Return "42"
End Function
then the SomeFunction(AnotherFunction(), YetAnotherFunction())
(this time's "qualifying expression") would be the part is "not evaluated". None of the SomeFunction
, AnotherFunction
, YetAnotherFunction
would be called, and nothing would appear in the console.
The purpose of an enum is to guide your fellow programmers in regard of what values are allowed. When you declare Dim c as colours
, you establish what should and shouldn't be stored in c
. It is not different from declaring c As Integer
so that someone could not store "asd"
in it.
Similarly, Dim c As colours = 5
would raise a compile-time error, because 5
is an Integer
rather than a colours
. So you would be nudged into Dim c A colours = colours.Red
.
I assume the error does not show for you because you have Option Strict Off
. You should set it to On
.
CodePudding user response:
The point of an Enum
is to provide a limited list of valid values in a particular scenario. The underlying type is numeric, so it is very efficient for the system to work with, but the field labels are descriptive and make it very easy for the developer to work with. You could use String
to make it just as easy for the developer but then you lose the efficiency and you also lose the ability to limit the range of values. Similarly, you could use a numeric data type to retain the efficiency but you then lose the user-friendliness and you also again lose the ability to limit the range of values.
As for using the values, it doesn't make sense to have a field of type colours
and then access colours.Red
via that field. That would be like having a field of type DateTime
and then accessing the DateTime.TryParse
method via that field. There would be no logic to that. You assign a DateTime
value to the field, you get a DateTime
value from the field and you compare that value to other DateTime
values, but you access the TryParse
method via the type, not an instance of that type. The same goes for a field on an Enum
type: you assign a value to it, you get a value from it, you compare that value to other values of the same type but you access the fields of the type via the type itself.
Let's fix your type for a start. Firstly, an Enum
is a first class type, just like classes, structures and modules. Don't declare an Enum
inside another type. Like any other type, the only good reason to nest an Enum
inside another type is if you're going to declare it Private
and only use it inside the other type. In your case, create a new code file and declare your Enum
in that. Generally speaking, you should have one type per code file. I tend to break that rule with Enums
because they are generally very small. For that reason, I have one code file named Enumerations.vb and I declare all my Enums
there.
As for the type itself:
Enum Colour
Red = 5
Blue
Green
End Enum
Like all types, the name should begin with an upper-case letter. There's generally no good reason to specify the numeric type because it will default to Integer
and that is the most efficient numeric type for the system to deal with. The tiny bit of storage space you save by using Byte
or Short
is generally pointless. The name should also be singular except in specific cases that I will cover later. I've left in your initialisation of the Red
field but generally that's pointless too. Unless the numbers mean something specific beyond the type itself, just accept the defaults.
Generally speaking, a field of an Enum
type will contain one value. In this case, a Colour
will be either Red
or Blue
or Green
. There are situations where you may want to store composite values though, e.g. a Permissions
type where a user can have zero, one or more permissions. In such cases, you should make three changes:
- The name should be plural rather than singular.
- The
Flags
attribute should be appliedto the type. - The values should be explicitly set to powers of 2.
<Flags>
Enum Permissions
Something = 1
SomethingElse = 2
YetAnotherThing = 4
End Enum
Consumers of the type should be able to tell whether composite values are supported or not by whether the name is plural or singular. Unfortunately, this rule was not always followed for older types. The Flags
attribute will cause the multiple values to be displayed sensibly when you call ToString
on a composite value. The powers of 2 mean that each value occupies one and only one bit within a numeric value, so composite values are never ambiguous.
To answer your questions explicitly:
- The qualifying expression in
c.Red
isc
. Anything before a dot qualifies what comes after, i.e. there could be a multitude of members namedRed
but the expression before the dot qualifies exactly which one of those is being referred to. In cases like this,Red
is not a member of a specific instance ofcolours
but rather of thecolours
type itself, so the specific instance you use to qualify is ignored and the type is used. - Why is the concept of a data type harder to grasp here than anywhere else. You have no trouble understanding why someone would want to store a
String
object in aString
variable, do you? You have no trouble understanding why someone would want to store anInteger
value in anInteger
variable, do you? Why is this any different? Take a look at theMessageBox.Show
method and the variousEnums
that it takes to control how the message is displayed. In your specific example, let's say that you were representing pixels in a TV that can be red, green or blue. YourPixel
class might have a property of typeColour
and you can then set that property toColour.Red
,Colour.Green
orColour.Blue
. It provides information of a specific type, just like any other type.