VB Curioddities #1: Enum, Enum, my kingdom for an Enum.Parse

VB Curioddities #1: Enum, Enum, my kingdom for an Enum.Parse

Rate This
  • Comments 20

Hey folks, my name's Kit George and I've joined the VB team from the CLR. VB is after all, the best language, so of course, it makes sense to work directly on it!

Like all languages, VB has it's little 'oddities', so i thought i would start a series to present a few of these. These are little (and perhaps large) curious VB things that you may or may not have noticed. Either way, you can bring them up at the water cooler to impress everyone with your VB knowledge.

I thought I would start with an interesting oddity surrounding Enum.Parse. Enum is of course, a keyword in Visual Basic, since it was already a keyword in days prior to .NET. This creates an interesting conflict between the Enum class (Enum is itself, a class in the System namespace of .NET), and the keyword. Why it becomes interesting is that the Enum class has some Shared members on it, most interestingly, Parse. You would normally invoke such a method with code like this:

  Public Class Test
  Public shared Sub Main()

    Dim ct as CarType = Enum.Parse(GetType(CarType), "Sedan")
    MsgBox(ct)

  End Sub
 End Class

 Enum CarType
  Sedan = 1
  Sports = 2
  SUV = 3
 End Enum

However, because Enum is a keyword, you'll get an exception on the above code because the 'Enum.Parse' line is treated as if you're trying to declare an Enum by the compiler. You could workaround this easily by simply doing the followin. The square brackets around the name Enum tell the compiler to NOT treat it like a keyword, therefore, it treats it like a class call:

    Dim ct as CarType = [Enum].Parse(GetType(CarType), "Sedan")

But in addition to the above syntax, and in order to make it even simpler, VB allows you to use the actual name of the Enum (CarType in this case) to invoke the Parse method. To all intents and purpose, this LOOKS like a Shared method call, so it should be supported:

    Dim ct as CarType = CarType.Parse(GetType(CarType), "Sedan")

Now here's where it gets interesting. Under the hood, the above line is actually turned into a call through to [Enum].Parse. That is, the CarType.Parse call has nothing to do with the CarType Enum itself, instead, it simply becomes a standard [Enum].Parse call. Therefore, what if I did something like this:

 Enum BoatType
  Trawler = 1
  SpeedBoat = 2
  CruiseShip = 3
 End Enum

    Dim ct as CarType = BoatType.Parse(GetType(CarType), "Sedan")

Surely this wouldn't compile, right? After all, it looks like we're parsing on the BoatType class, into a CarType type: that surely doesn't work.

But remember that CarType.Parse simply got turned into [Enum].Parse? Well, ANY other Enum call is the same. So BoatType.Parse, really just becomes [Enum].Parse. And this works just fine! So the above code compiles and runs just fine. Of course, I would not suggest under any circumstances, that you write your code this way, it is odd to read.

Note that of course, this will NOT work:

    Dim bt as BoatType = BoatType.Parse(GetType(CarType), "Trawler")

The reason is that the KEY things for the transformation to succeed are that a) the type on the left of the assignment, and the type in the first parameter to the Parse method, match (they don't in the above case, which is what causes it to fail), and b) that the string can be converted to the specified type (That is, it is a member of the specified Enum. In the above case it can, but of course, we're gonna fail here because of item a) anyway).

At the end of the day, my own preference IS to use the name of the specific Enum, but simply use the same name as the Enum I'm parsing into. But I do like messing with people from time to time with this one ;-).

Leave a Comment
  • Please add 2 and 2 and type the answer here:
  • Post
  • Forget the Parse method and the Extensions for a moment. The only thiing I'm really interested in is on why we cannot have methods on enumerations.

    "So in the stack frame you don't even have an enum there, just an Int32, so you aren't really going to be able ot call a virtual method on it. "

    No, you don't get just an Int32 on the stack. According to ILDASM, we get the actual enum on the stack...

       Sub Foo()
           Dim A As MyEnum = MyEnum.boat
           Dim B As Integer = 1

     .locals init ([0] valuetype WinForm_Scratch.Boom/MyEnum A,
              [1] int32 B)

    Futhermore, all enum's have a field called value__ that stores the value in the underlying type.

    Secondly, any non-overriding method I write won't be virtual. Enums are always sealed, so the call is resolved statically at compile-time. (Unless of course you box the value, in which case I guess it gets resolved just like any other boxed value.)

    Finally, we can already make method calls on enums such as ToString and GetTypeCode. And yes, these even work when the value is boxed.

    I know that the Common Type System explicitly says that enums cannot have methods, but it doesn't say why. From what I have read so far, it is a convention rather than a technical requirement.
  • ToString, like Parse is on the base, System.Enum.

    As to Enum's value memeber, it can in fact have any name but the requirements are there be only one field, that auto layout be applied and the field be an intrinsic type of the integer family.  The reason for this is because you are allowed to use the underlying type instead of the enum anytime you like in IL, so the enum's stack alignment needs to match that of the intrinsic type.

    So basically the two problems you'd face if you tried to enforce a method being in an enum is that every language compiler in the world would need to implement that. That is, you'd have ot enforce a must override contract in System.Enum which also technically is illegal <g>
    Secondly, many languages compile the underlying value as a constant not a call to the enum static field. They woudl all have ot change.
    So such a change would be (a) a breakign change in the framework, and (b) require extensive work by all language compilers.  You have to weigh that cost against the benifit.  you also need to compare that cost to other approaches such as non breaking low cost alternatives such as extension methods.



  • > So basically the two problems you'd face if you tried to enforce a method being in an enum is that every language compiler in the world would need to implement that. That is, you'd have ot enforce a must override contract in System.Enum which also technically is illegal <g>

    What part of "Forget the Parse method" did you not understand? The ship has sailed on that one. Fine, whatever. I don't care. What about my actual question...

    WHY AM I NOT ALLOWED TO CREATE MY OWN METHODS INSIDE ENUM TYPES?

  • Jonathan,  what do you mean by :
    > What part of "Forget the Parse method" did you not understand?
    You said:
    > Finally, we can already make method calls on enums such as ToString and
    > GetTypeCode.
    So I was pointign out to you they are on the base class the same as Parse is.  What exacly was your point if you want to forget Parse and now talk about yet another method on the base class. as I said, ToString is just the same as Parse is.

    As to why you are not allowed to create your own methods, perhaps Kit can shed more light on that than I, but I think i've laready given you the HINT there.  to put it simply, enums and their underlying type, e.g Int32, are fully interchangeable. This is why enums have no constraint checking as they are treated as an intrinsic value such as Int32.  This offers great perfromance benifits at runtime, while still giving the design time goodness

  • Perhaps I am being a bit dense on this. Kit, can you expand on this?
Page 2 of 2 (20 items) 12