Enumerations

Now we'll examine how you declare enumerations in IL. Enumerations are relatively straightforward - there aren't really any fundamental new concepts involved, but there are various restrictions on enums that are imposed by the .NET Framework.

In high-level languages, you'll be used to declaring enums like this:

 // C# code enum TimeOfDay { Am, Pm }; 

And you'll no doubt be aware that what you are actually declaring is a type that is derived from System.Enum, with System.Enum providing all that clever stuff you're used to, such as converting the values to or from a string. However, an awful lot of what's going on is hidden by the special enum syntax of high-level languages. In practice, some of the support for defining enums in languages such as C#, MC++, and VB falls naturally out of the class structure and the methods implemented by System.Enum, but there is also some help provided by the .NET Framework which has some specific knowledge of enums hard-coded into it.

Supplied by the class structure:

  • Each enum you define is in fact a value type that derives from System.Enum.

  • Methods such as ToString() and Parse() are implemented by System.Enum and hence inherited by your enum.

  • The enumerated values (Am and Pm in the above example) are public static literal fields of your enumeration class. These fields must be of the same type as the enum in which they have been defined (in other words, with the above example, Am and Pm are each public static literal TimeOfDay fields; literal in IL means the same as const in C# or C++ and Const in VB.)

  • There is also a private instance field. This instance field is the 'value' of any instances of the enum you declare, and is always called value_. The type of this field is the underlying type (if your enumeration class is based on int32, the default underlying type, the value_ field will also be of type int32).

Supplied by the .NET runtime:

  • The runtime knows about the value__instance field. (This field must be decorated with the specialname and rtspecialname flags.)

  • The runtime knows that types derived from System.Enum form a special category of types, and imposes restrictions on them to ensure they conform to the enum model. In particular, each enum must implement the value__ instance field, and cannot implement any other instance members.

In addition to all this, Visual Studio .NET supplies extra user-interface features such as enum-specific IntelliSense to assist in programming with enums.

Let's see how all this works by writing a short IL program that defines an AmOrPm enum as above, instantiates it, and prints the value of the AmOrPm instance. Here's the enum definition:

 .namespace Wrox.AdvDotNet.EnumDemo {    .class public auto ansi sealed AmOrPm extends [mscorlib]System.Enum    {       .field public specialname rtspecialname int32 value__       .field public static literal valuetype Wrox.AdvDotNet.EnumDemo.AmOrPm                                                             Am = int32(0x0)       .field public static literal valuetype Wrox.AdvDotNet.EnumDemo.AmOrPm                                                             Pm = int32(0x1)    } } 

The code for the definition essentially illustrates the points we've already made. The value__ field is present to represent the value of an instance of the enum, and the various enumerated values exist as static fields. When declaring literal (const) values in IL, the actual literal value is always supplied after the = sign, in a format that should be clear from the above code.

Now let's see how we use the enum:

      .method static void Main() cil managed      {          .maxstack 2          .entrypoint          .locals init (valuetype Wrox.AdvDotNet.EnumDemo.AmOrPm time)          // Initialize the local variable to 1 (=Pm)          ldc.i4.1          stloc.0          // Write out time as a number          ldstr    "Value of enum (as number) is "          call     void [mscorlib]System.Console::Write(string)          ldloc.0          call     void [mscorlib]System.Console::WriteLine(int32)          // Write out time as a string          ldstr    "Value of enum (as string) is "          call     void [mscorlib]System.Console::Write(string)          ldloc.0          box      Wrox.AdvDotNet.EnumDemo.AmOrPm          call     instance string [mscorlib]System.Enum::ToString()          call     void [mscorlib]System.Console::WriteLine(string)          ret      } 

Notice first of all that we initialize the enum from an int32 value that we have pushed onto the stack:

 ldc.i4.1 stloc.0 

This code looks at first sight like it breaks type-safety rules: it pushes an int32 into a memory location occupied by an AmOrPm object. The fact that it passes peverify is an example of the support for enums that is built into the CLR. The CLR knows that enums are for all practical purposes just integers (or whatever the underlying type is), so the verification process considers an enum as being equivalent to its underlying type.

Next we load up the enum and pass its value to Console.WriteLine() to display its numerical value. Given what we've just said about the CLR, this part of the code shouldn't surprise us. But there is a surprise in store in the code that displays it as a string: we box the value before calling System.Enum.ToString() - apparently in direct contradiction to what we said earlier about not passing object references to boxed value types to methods:

 ldloc.0 box      Wrox.AdvDotNet.EnumDemo,AmOrPm call     instance string [mscorlib]System.Enum::ToString() 

The reason for this apparent discrepancy is quite simple: although types that are derived from System.Enum are always automatically enumerations and therefore value types, System.Enum itself is a reference type! So, because we are calling a method (Enum.ToString()) defined on a reference type, we need to box our enum instance and pass in an object reference to the method. The same oddity occurs for other value types: any type derived from ValueType (apart from System.Enum) is a value type, but System.ValueType itself is a reference type. Although this sounds counter-intuitive, there are good reasons for this. In particular, ValueType and Enum need to be reference types in order to allow you to derive other types from them. We'll explore this issue in more detail in Chapter 3, when we examine how value, reference, and boxed types are implemented in practice.



Advanced  .NET Programming
Advanced .NET Programming
ISBN: 1861006292
EAN: 2147483647
Year: 2002
Pages: 124

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net