Creating Value Types with Enumerations and Structures - Microsoft® Visual C#® 2012 Step by Step (2012)

Microsoft® Visual C#® 2012 Step by Step (2012)

Chapter 9. Creating Value Types with Enumerations and Structures

After completing this chapter, you will be able to

§ Declare an enumeration type.

§ Create and use an enumeration type.

§ Declare a structure type.

§ Create and use a structure type.

§ Explain the differences in behavior between a structure and a class.

In Chapter 8, you learned about the two fundamental types that exist in Microsoft Visual C#: value types and reference types. A value type variable holds its value directly on the stack, whereas a reference type variable holds a reference to an object on the heap. In Chapter 7, you learned how to create your own reference types by defining classes. In this chapter, you’ll learn how to create your own value types.

C# supports two kinds of value types: enumerations and structures. We’ll look at each of them in turn.

Working with Enumerations

Suppose you want to represent the seasons of the year in a program. You could use the integers 0, 1, 2, and 3 to represent spring, summer, fall, and winter, respectively. This system would work, but it’s not very intuitive. If you used the integer value 0 in code, it wouldn’t be obvious that a particular 0 represented spring. It also wouldn’t be a very robust solution. For example, if you declare an int variable named season, there is nothing to stop you from assigning it any legal integer value outside of the set 0, 1, 2, or 3. C# offers a better solution. You can create an enumeration (sometimes called an enum type), whose values are limited to a set of symbolic names.

Declaring an Enumeration

You define an enumeration by using the enum keyword, followed by a set of symbols identifying the legal values that the type can have, enclosed between braces. Here’s how to declare an enumeration named Season whose literal values are limited to the symbolic names Spring, Summer, Fall, and Winter:

enum Season { Spring, Summer, Fall, Winter }

Using an Enumeration

After you have declared an enumeration, you can use it in exactly the same way as any other type. If the name of your enumeration is Season, you can create variables of type Season, fields of type Season, and parameters of type Season, as shown in this example:

enum Season { Spring, Summer, Fall, Winter }

class Example

{

public void Method(Season parameter) // method parameter example

{

Season localVariable; // local variable example

...

}

private Season currentSeason; // field example

}

Before you can read the value of an enumeration variable, it must be assigned a value. You can assign a value that is defined by the enumeration only to an enumeration variable. For example:

Season colorful = Season.Fall;

Console.WriteLine(colorful); // writes out 'Fall'

NOTE

As you can with all value types, you can create a nullable version of an enumeration variable by using the ? modifier. You can then assign the null value, as well the values defined by the enumeration, to the variable:

Season? colorful = null;

Notice that you have to write Season.Fall rather than just Fall. All enumeration literal names are scoped by their enumeration type. This is useful because it allows different enumerations to coincidentally contain literals with the same name.

Also, notice that when you display an enumeration variable by using Console.WriteLine, the compiler generates code that writes out the name of the literal whose value matches the value of the variable. If needed, you can explicitly convert an enumeration variable to a string that represents its current value by using the built-in ToString method that all enumerations automatically contain. For example:

string name = colorful.ToString();

Console.WriteLine(name); // also writes out 'Fall'

Many of the standard operators you can use on integer variables can also be used on enumeration variables (except the bitwise and shift operators, which are covered in Chapter 16). For example, you can compare two enumeration variables of the same type for equality by using the equality operator (==), and you can even perform arithmetic on an enumeration variable (although the result might not always be meaningful!).

Choosing Enumeration Literal Values

Internally, an enumeration type associates an integer value with each element of the enumeration. By default, the numbering starts at 0 for the first element and goes up in steps of 1. It’s possible to retrieve the underlying integer value of an enumeration variable. To do this, you must cast it to its underlying type. Remember from the discussion of unboxing in Chapter 8 that casting a type converts the data from one type to another as long as the conversion is valid and meaningful. The following code example writes out the value 2 and not the word Fall (in the Season enumerationSpring is 0, Summer 1, Fall 2, and Winter 3):

enum Season { Spring, Summer, Fall, Winter }

...

Season colorful = Season.Fall;

Console.WriteLine((int)colorful); // writes out '2'

If you prefer, you can associate a specific integer constant (such as 1) with an enumeration literal (such as Spring), as in the following example:

enum Season { Spring = 1, Summer, Fall, Winter }

IMPORTANT

The integer value with which you initialize an enumeration literal must be a compile-time constant value (such as 1).

If you don’t explicitly give an enumeration literal a constant integer value, the compiler gives it a value that is 1 greater than the value of the previous enumeration literal, except for the very first enumeration literal, to which the compiler gives the default value 0. In the preceding example, the underlying values of Spring, Summer, Fall, and Winter are now 1, 2, 3, and 4.

You are allowed to give more than one enumeration literal the same underlying value. For example, in the United Kingdom, Fall is referred to as Autumn. You can cater for both cultures as follows:

enum Season { Spring, Summer, Fall, Autumn = Fall, Winter }

Choosing an Enumeration’s Underlying Type

When you declare an enumeration, the enumeration literals are given values of type int. You can also choose to base your enumeration on a different underlying integer type. For example, to declare that Season’s underlying type is a short rather than an int, you can write this:

enum Season : short { Spring, Summer, Fall, Winter }

The main reason for doing this is to save memory; an int occupies more memory than a short, and if you do not need the entire range of values available to an int, using a smaller data type can make sense.

You can base an enumeration on any of the eight integer types: byte, sbyte, short, ushort, int, uint, long, or ulong. The values of all the enumeration literals must fit inside the range of the chosen base type. For example, if you base an enumeration on the byte data type, you can have a maximum of 256 literals (starting at 0).

Now that you know how to declare an enumeration, the next step is to use it. In the following exercise, you will work with a console application to declare and use an enumeration that represents the months of the year.

Create and use an enumeration

1. Start Microsoft Visual Studio 2012 if it is not already running.

2. Open the StructsAndEnums project, located in the \Microsoft Press\Visual CSharp Step By Step\Chapter 9\Windows X\StructsAndEnums folder in your Documents folder.

3. In the Code and Text Editor window, display the Month.cs file.

The source file is empty apart from the declaration of a namespace called StructsAndEnums and a // TODO: comment.

4. Delete the // TODO: comment and add an enumeration named Month for modeling the months of the year inside the StructsAndEnums namespace. The 12 enumeration literals for Month are January through December.

5. namespace StructsAndEnums

6. {

7. enum Month

8. {

9. January, February, March, April,

10. May, June, July, August,

11. September, October, November, December

12. }

}

13.Display the Program.cs file in the Code and Text Editor window.

As in the exercises in previous chapters, the Main method calls the doWork method and traps any exceptions that occur.

14.In the Code and Text Editor window, add a statement to the doWork method to declare a variable named first of type Month and initialize it to Month.January. Add another statement to write the value of the first variable to the console.

The doWork method should look like this:

static void doWork()

{

Month first = Month.January;

Console.WriteLine(first);

}

NOTE

When you type the period following Month, Microsoft IntelliSense will automatically display all the values in the Month enumeration.

15.On the DEBUG menu, click Start Without Debugging.

Visual Studio 2012 builds and runs the program. Confirm that the word January is written to the console.

16.Press Enter to close the program and return to the Visual Studio 2012 programming environment.

17.Add two more statements to the doWork method to increment the first variable and display its new value to the console, as shown in bold here:

18.static void doWork()

19.{

20. Month first = Month.January;

21. Console.WriteLine(first);

22. first++;

23. Console.WriteLine(first);

}

24.On the DEBUG menu, click Start Without Debugging.

Visual Studio 2012 builds and runs the program. Confirm that the words January and February are written to the console.

Notice that performing a mathematical operation (such as the increment operation) on an enumeration variable changes the internal integer value of the variable. When the variable is written to the console, the corresponding enumeration value is displayed.

25.Press Enter to close the program and return to the Visual Studio 2012 programming environment.

26.Modify the first statement in the doWork method to initialize the first variable to Month.December, as shown in bold here:

27.static void doWork()

28.{

29. Month first = Month.December;

30. Console.WriteLine(first);

31. first++;

32. Console.WriteLine(first);

}

33.On the DEBUG menu, click Start Without Debugging.

Visual Studio 2012 builds and runs the program. This time, the word December is written to the console, followed by the number 12.

image with no caption

Although you can perform arithmetic on an enumeration, if the results of the operation are outside the range of values defined for the enumerator, all the runtime can do is interpret the value of the variable as the corresponding integer value.

34.Press Enter to close the program and return to the Visual Studio 2012 programming environment.

Working with Structures

You saw in Chapter 8 that classes define reference types that are always created on the heap. In some cases, the class can contain so little data that the overhead of managing the heap becomes disproportionate. In these cases, it is better to define the type as a structure. A structure is a value type. Because structures are stored on the stack, as long as the structure is reasonably small, the memory management overhead is often reduced.

Like a class, a structure can have its own fields, methods, and (with one important exception discussed later in this chapter) constructors.

COMMON STRUCTURE TYPES

You might not have realized it, but you have already used structures in previous exercises in this book. In C#, the primitive numeric types int, long, and float are aliases for the structures System.Int32, System.Int64, and System.Single, respectively. These structures have fields and methods, and you can actually call methods on variables and literals of these types. For example, all of these structures provide a ToString method that can convert a numeric value to its string representation. The following statements are all legal in C#:

int i = 99;

Console.WriteLine(i.ToString());

Console.WriteLine(55.ToString());

float f = 98.765F;

Console.WriteLine(f.ToString());

Console.WriteLine(98.765F.ToString());

You don’t see this use of the ToString method often, because the Console.WriteLine method calls it automatically when it is needed. It is more common to use some of the static methods exposed by these structures. For example, in earlier chapters you used the static int.Parse method to convert a string to its corresponding integer value. What you are actually doing is invoking the Parsemethod of the Int32 structure:

string s = "42";

int i = int.Parse(s); // exactly the same as Int32.Parse

These structures also include some useful static fields. For example, Int32.MaxValue is the maximum value that an int can hold, and Int32.MinValue is the smallest value you can store in an int.

The following table shows the primitive types in C# and their equivalent types in the Microsoft .NET Framework. Notice that the string and object types are classes (reference types) rather than structures.

Keyword

Type equivalent

Class or structure

bool

System.Boolean

Structure

byte

System.Byte

Structure

decimal

System.Decimal

Structure

double

System.Double

Structure

float

System.Single

Structure

int

System.Int32

Structure

long

System.Int64

Structure

object

System.Object

Class

sbyte

System.SByte

Structure

short

System.Int16

Structure

string

System.String

Class

uint

System.UInt32

Structure

ulong

System.UInt64

Structure

ushort

System.UInt16

Structure

Declaring a Structure

To declare your own structure type, you use the struct keyword followed by the name of the type, followed by the body of the structure between opening and closing braces. Syntactically, the process is similar to declaring a class. For example, here is a structure named Time that contains threepublic int fields named hours, minutes, and seconds:

struct Time

{

public int hours, minutes, seconds;

}

As with classes, making the fields of a structure public is not advisable in most cases; there is no way to control the values held in public fields. For example, anyone could set the value of minutes or seconds to a value greater than 60. A better idea is to make the fields private and provide your structure with constructors and methods to initialize and manipulate these fields, as shown in this example:

struct Time

{

private int hours, minutes, seconds;

...

public Time(int hh, int mm, int ss)

{

this.hours = hh % 24;

this.minutes = mm % 60;

this.seconds = ss % 60;

}

public int Hours()

{

return this.hours;

}

}

NOTE

By default, you cannot use many of the common operators on your own structure types. For example, you cannot use operators such as the equality operator (==) and the inequality operator (!=) on your own structure type variables. However, you can use the builtin Equals() method exposed by all structures to compare them, and you can also explicitly declare and implement operators for your own structure types. The syntax for doing this is covered in Chapter 22

When you copy a value type variable, you get two copies of the value. In contrast, when you copy a reference type variable, you get two references to the same object. In summary, use structures for small data values where it’s just as or nearly as efficient to copy the value as it would be to copy an address. Use classes for more complex data that is too big to copy efficiently.

TIP

Use structures to implement simple concepts whose main feature is their value rather than the functionality that they provide.

Understanding Structure and Class Differences

A structure and a class are syntactically similar, but there are a few important differences. Let’s look at some of these variances:

§ You can’t declare a default constructor (a constructor with no parameters) for a structure. The following example would compile if Time were a class, but because Time is a structure, it does not:

§ struct Time

§ {

§ public Time() { ... } // compile-time error

§ ...

}

The reason you can’t declare your own default constructor for a structure is that the compiler always generates one. In a class, the compiler generates the default constructor only if you don’t write a constructor yourself. The compiler-generated default constructor for a structure always sets the fields to 0, false, or null—just as for a class. Therefore, you should ensure that a structure value created by the default constructor behaves logically and makes sense with these default values. This has some ramifications that you will explore in the next exercise.

You can initialize fields to different values by providing a nondefault constructor. However, when you do this, your nondefault constructor must explicitly initialize all fields in your structure; the default initialization no longer occurs. If you fail to do this, you’ll get a compile-time error. For example, although the following example would compile and silently initialize seconds to 0 if Time were a class, because Time is a structure, it fails to compile:

struct Time

{

private int hours, minutes, seconds;

...

public Time(int hh, int mm)

{

this.hours = hh;

this.minutes = mm;

} // compile-time error: seconds not initialized

}

§ In a class, you can initialize instance fields at their point of declaration. In a structure, you cannot. The following example would compile if Time were a class, but because Time is a structure, it causes a compile-time error:

§ struct Time

§ {

§ private int hours = 0; // compile-time error

§ private int minutes;

§ private int seconds;

§ ...

}

The following table summarizes the main differences between a structure and a class.

Question

Structure

Class

Is this a value type or a reference type?

A structure is a value type.

A class is a reference type.

Do instances live on the stack or the heap?

Structure instances are called values and live on the stack.

Class instances are called objects and live on the heap.

Can you declare a default constructor?

No.

Yes.

If you declare your own constructor, will the compiler still generate the default constructor?

Yes.

No.

If you don’t initialize a field in your own constructor, will the compiler automatically initialize it for you?

No.

Yes.

Are you allowed to initialize instance fields at their point of declaration?

No.

Yes.

There are other differences between classes and structures concerning inheritance. These differences are covered in Chapter 12

Declaring Structure Variables

After you have defined a structure type, you can use it in exactly the same way as any other type. For example, if you have defined the Time structure, you can create variables, fields, and parameters of type Time, as shown in this example:

struct Time

{

private int hours, minutes, seconds;

...

}

class Example

{

private Time currentTime;

public void Method(Time parameter)

{

Time localVariable;

...

}

}

NOTE

As with enumerations, you can create a nullable version of a structure variable by using the ? modifier. You can then assign the null value to the variable:

Time? currentTime = null;

Understanding Structure Initialization

Earlier in this chapter, you saw how the fields in a structure can be initialized by using a constructor. If you call a constructor, the various rules described earlier guarantee that all the fields in the structure will be initialized:

Time now = new Time();

The following graphic depicts the state of the fields in this structure:

image with no caption

However, because structures are value types, you can also create structure variables without calling a constructor, as shown in the following example:

Time now;

This time, the variable is created but its fields are left in their uninitialized state. The following graphic depicts the state of the fields in the now variable. Any attempt to access the values in these fields will result in a compiler error:

image with no caption

Note that in both cases, the Time variable is created on the stack.

If you’ve written your own structure constructor, you can also use that to initialize a structure variable. As explained earlier in this chapter, a structure constructor must always explicitly initialize all its fields. For example:

struct Time

{

private int hours, minutes, seconds;

...

public Time(int hh, int mm)

{

hours = hh;

minutes = mm;

seconds = 0;

}

}

The following example initializes now by calling a user-defined constructor:

Time now = new Time(12, 30);

The following graphic shows the effect of this example:

image with no caption

It’s time to put this knowledge into practice. In the following exercise, you will create and use a structure to represent a date.

Create and use a structure type

1. In the StructsAndEnums project, display the Date.cs file in the Code and Text Editor window.

2. Add a structure named Date inside the StructsAndEnums namespace.

This structure should contain three private fields: one named year of type int, one named month of type Month (using the enumeration you created in the preceding exercise), and one named day of type int. The Date structure should look exactly as follows:

struct Date

{

private int year;

private Month month;

private int day;

}

Consider the default constructor that the compiler will generate for Date. This constructor sets the year to 0, the month to 0 (the value of January), and the day to 0. The year value 0 is not valid (because there was no year 0), and the day value 0 is also not valid (because each month starts on day 1). One way to fix this problem is to translate the year and day values by implementing the Date structure so that when the year field holds the value Y, this value represents the year Y + 1900 (or you can pick a different century if you prefer), and when the day field holds the value D, this value represents the day D + 1. The default constructor will then set the three fields to values that represent the date 1 January 1900.

If you could override the default constructor and write your own, this would not be an issue, as you could then initialize the year and day fields directly to valid values. You cannot do this, though, and so you have to implement the logic in your structure to translate the compiler-generated default values into meaningful values for your problem domain.

However, although you cannot override the default constructor, it is still good practice to define nondefault constructors to allow a user to explicitly initialize the fields in a structure to meaningful nondefault values.

3. Add a public constructor to the Date structure. This constructor should take three parameters: an int named ccyy for the year, a Month named mm for the month, and an int named dd for the day. Use these three parameters to initialize the corresponding fields. A year field with the valueY represents the year Y + 1900, so you need to initialize the year field to the value ccyy – 1900. A day field with the value D represents the day D + 1, so you need to initialize the day field to the value dd – 1.

The Date structure should now look like this (with the constructor shown in bold):

struct Date

{

private int year;

private Month month;

private int day;

public Date(int ccyy, Month mm, int dd)

{

this.year = ccyy - 1900;

this.month = mm;

this.day = dd - 1;

}

}

4. Add a public method named ToString to the Date structure after the constructor. This method takes no arguments and returns a string representation of the date. Remember, the value of the year field represents year + 1900, and the value of the day field represents day + 1.

NOTE

The ToString method is a little different from the methods you have seen so far. Every type, including structures and classes that you define, automatically has a ToString method whether or not you want it. Its default behavior is to convert the data in a variable to a string representation of that data. Sometimes, the default behavior is meaningful; other times, it is less so. For example, the default behavior of the ToString method generated for the Date struct simply generates the string “StructsAndEnums.Date”. To quote Zaphod Beeblebrox in The Restaurant at the End of the Universe by Douglas Adams (Pan Macmillan, 1980), this is “shrewd, but dull.” You need to define a new version of this method that overrides the default behavior by using the overridekeyword. Overriding methods are discussed in more detail in Chapter 12.

The ToString method should look like this:

public override string ToString()

{

string data = String.Format("{0} {1} {2}", this.month, this.day + 1,

this.year + 1900);

return data;

}

The Format method of the String class enables you to format data. It operates in a similar manner to the Console.WriteLine method, except that rather than displaying data to the console, it returns the formatted result as a string. In this example, the positional parameters are replaced with the text representations of the values of the month field, the expression this.day + 1, and the expression this.year + 1900. The ToString method returns the formatted string as its result.

5. Display the Program.cs file in the Code and Text Editor window.

6. In the doWork method, comment out the existing four statements. Add code to the doWork method to declare a local variable named defaultDate, and initialize it to a Date value constructed by using the default Date constructor. Add another statement to doWork to write the defaultDatevariable to the console by calling Console.WriteLine.

NOTE

The Console.WriteLine method automatically calls the ToString method of its argument to format the argument as a string.

The doWork method should now look like this:

static void doWork()

{

...

Date defaultDate = new Date();

Console.WriteLine(defaultDate);

}

NOTE

When you type the new keyword, IntelliSense automatically detects that there are two constructors available for the Date type.

7. On the DEBUG menu, click Start Without Debugging to build and run the program. Verify that the date January 1 1900 is written to the console.

8. Press the Enter key to return to the Visual Studio 2012 programming environment.

9. In the Code and Text Editor window, return to the doWork method, and add two more statements. In the first statement, declare a local variable named weddingAnniversary and initialize it to July 4 2012. (I actually did get married on Independence Day.) In the second statement, write the value of weddingAnniversary to the console.

The doWork method should now look like this:

static void doWork()

{

...

Date weddingAnniversary = new Date(2012, Month.July, 4);

Console.WriteLine(weddingAnniversary);

}

10.On the DEBUG menu, click Start Without Debugging. Confirm that the date July 4 2012 is written to the console below the previous information.

11.Press Enter to close the program and return to Visual Studio 2012.

Copying Structure Variables

You’re allowed to initialize or assign one structure variable to another structure variable, but only if the structure variable on the right side is completely initialized (that is, if all its fields are populated with valid data rather than undefined values). The following example compiles because nowis fully initialized. The graphic shows the results of performing such an assignment (this image was created on March 19, 2012).

Date now = new Date();

Date copy = now;

image with no caption

The following example fails to compile because now is not initialized:

Date now;

Date copy = now; // compile-time error: now has not been assigned

When you copy a structure variable, each field on the left side is set directly from the corresponding field on the right side. This copying is done as a fast, single operation that copies the contents of the entire structure and that never throws an exception. Compare this behavior with the equivalent action if Time were a class, in which case both variables (now and copy) would end up referencing the same object on the heap.

NOTE

C++ programmers should note that this copy behavior cannot be customized.

In the final exercise in this chapter, you will contrast the copy behavior of a structure with that of a class.

Compare the behavior of a structure and a class

1. In the StructsAndEnums project, display the Date.cs file in the Code and Text Editor window.

2. Add the following method to the Date structure. This method advances the date in the structure by one month. If, after advancing the month, the value of the month field has moved beyond December, this code resets the month to January and advances the value of the year field by 1.

3. public void AdvanceMonth()

4. {

5. this.month++;

6. if (this.month == Month.December + 1)

7. {

8. this.month = Month.January;

9. this.year++;

10. }

}

11.Display the Program.cs file in the Code and Text Editor window.

12.In the doWork method, comment out the first two statements that create and display the value of the defaultDate variable.

13.Add the following code shown in bold to the end of the doWork method. This code creates a copy of the weddingAnniversary variable called weddingAnniversaryCopy and prints out the value of this new variable.

14.static void doWork()

15.{

16. ...

17. Date weddingAnniversaryCopy = weddingAnniversary;

18. Console.WriteLine("Value of copy is {0}", weddingAnniversaryCopy);

}

19.Add the following statements shown in bold to the end of the doWork method. These statements call the AdvanceMonth method of the weddingAnniversary variable and then display the value of the weddingAnniversary and weddingAnniversaryCopy variables:

20.static void doWork()

21.{

22. ...

23. weddingAnniversary.AdvanceMonth();

24. Console.WriteLine("New value of weddingAnniversary is {0}", weddingAnniversary);

25. Console.WriteLine("Value of copy is still {0}", weddingAnniversaryCopy);

}

26.On the DEBUG menu, click Start Without Debugging to build and run the application. Verify that the console window displays the following messages:

27.July 4 2012

28.Value of copy is July 4 2012

29.New value of weddingAnniversary is August 4 2012

Value of copy is still July 4 2012

The first message displays the initial value of the weddingAnniversary variable (July 4 2012). The second message displays the value of the weddingAnniversaryCopy variable. You can see that it contains a the same date held in the weddingAnniversary variable (July 4 2012). The third message displays the value of the weddingAnniversary variable after changing the month to August (August 4 2012). The final statement displays the value of the weddingAnniversaryCopy variable. Notice that it has not changed from its original value of July 4 2012. The final message displays the value of the weddingAnniversaryCopy variable again. You can see that this has not changed and is still July 4 2012.

If Date was a class, creating a copy would reference the same object in memory as the original instance. Changing the month in the original instance would therefore also change the date referenced through the copy. You will verify this assertion in the following steps.

30.Press Enter and return to Visual Studio 2012.

31.Display the Date.cs file in the Code and Text Editor window.

32.Change the Date structure into a class, as shown in bold in the following code example:

33.class Date

34.{

35. ...

}

36.On the DEBUG menu, click Start Without Debugging to build and run the application again. Verify that the console window displays the following messages:

37.July 4 2012

38.Value of copy is July 4 2012

39.New value of weddingAnniversary is August 4 2012

Value of copy is still August 4 2012

The first three messages are the same as before. However, the fourth message shows that the value of the weddingAnniversaryCopy variable has changed to August 4 2012.

40.Press Enter and return to Visual Studio 2012.

STRUCTS AND COMPATIBILITY WITH THE WINDOWS RUNTIME ON WINDOWS 8

All C# applications execute by using the common language runtime (CLR) of the .NET Framework. The CLR is responsible for providing a safe and secure environment for your application code in the form of a virtual machine (if you have come from a Java background, this concept should be familiar to you). When you compile a C# application, the compiler converts your C# code into a set of instructions using a pseudo-machine code called the Common Intermediate Language (CIL). These are the instructions that are stored in an assembly. When you run a C# application, the CLR takes responsibility for converting the CIL instructions into real machine instructions that the processor on your computer can understand and execute. This whole environment is known as themanaged execution environment, and C# programs are frequently referred to as managed code. You can also write managed code in other languages supported by the .NET Framework, such as Visual Basic and F#.

On Windows 7 and earlier, you can additionally write unmanaged applications, also known as native code, based on the Win32 APIs, which are the APIs that interface directly with the Windows operating system (the CLR also converts many of the functions in the .NET Framework into Win32 API calls if you are running a managed application, although this process is totally transparent to your code). To do this, you can use a language such as C++. The .NET Framework enables you to integrate managed code into unmanaged applications and vice versa through a set of interoperability technologies. Detailing how these technologies work and how you use them is beyond the scope of this book—suffice to say that it was not always straightforward.

Windows 8 implements an alternative strategy, in the form of the Windows Runtime, or WinRT. WinRT provides a layer on top of the Win32 API (and other selected native Windows APIs) that is optimized for touch-based devices and user interfaces, such as those found on Windows 8 tablets. When you build a native application on Windows 8, you use the APIs exposed by WinRT rather than Win32. Similarly, the CLR on Windows 8 also uses WinRT; all managed code written by using C# or any other managed language is still executed by the CLR, but at run time the CLR converts your code into WinRT API calls rather than Win32. Between them, the CLR and WinRT are responsible for managing and running your code safely.

A primary purpose of WinRT is to simplify the interoperability between languages, enabling you to more easily integrate components developed by using different programming languages into a single seamless application. However, this simplicity comes at a cost, and you have to be prepared to make a few compromises based on the different feature sets of the various languages available. In particular, for historical reasons, although C++ supports structures, it does not recognize member functions. In C# terms, a member function is an instance method. So, if you are building C# structs that you want to package up in a library to make available to developers programming in C++ (or any other unmanaged language), these structs should not contain any instance methods. The same restriction applies to static methods in structs. If you want to include instance or static methods, you should convert your struct into a class. Additionally, structs cannot contain private fields, and all public fields must be C# primitive types, conforming value types, or strings.

WinRT also imposes some other restrictions on C# classes and structs if you want to make them available to native applications. Chapter 13 provides more information.

Summary

In this chapter, you saw how to create and use enumerations and structures. You learned some of the similarities and differences between a structure and a class, and you saw how to define constructors to initialize the fields in a structure. You also saw how to represent a structure as a string by overriding the ToString method.

§ If you want to continue to the next chapter

Keep Visual Studio 2012 running, and turn to Chapter 10.

§ If you want to exit Visual Studio 2012 now

On the FILE menu, click Exit. If you see a Save dialog box, click Yes and save the project.

Chapter 9 Quick Reference

To

Do this

Declare an enumeration

Write the keyword enum, followed by the name of the type, followed by a pair of braces containing a comma-separated list of the enumeration literal names. For example:

enum Season { Spring, Summer, Fall, Winter }

Declare an enumeration variable

Write the name of the enumeration on the left followed by the name of the variable, followed by a semicolon. For example:

Season currentSeason;

Assign an enumeration variable to a value

Write the name of the enumeration literal in combination with the name of the enumeration to which it belongs. For example:

currentSeason = Spring; // error

currentSeason = Season.Spring; // correct

Declare a structure type

Write the keyword struct, followed by the name of the structure type, followed by the body of the structure (the constructors, methods, and fields). For example:

struct Time

{

public Time(int hh, int mm, int ss)

{ ... }

...

private int hours, minutes, seconds;

}

Declare a structure variable

Write the name of the structure type, followed by the name of the variable, followed by a semicolon. For example:

Time now;

Initialize a structure variable to a value

Initialize the variable to a structure value created by calling the structure constructor. For example:

Time lunch = new Time(12, 30, 0);