C# 2012 step by step 学习笔记8 CHAPTER 9 Creating Value types with enumerations and Structures
C# 2012 step by step 学习笔记8 CHAPTER 9 Creating Value types with enumerations and Structures
things about
1. Declare an enumeration type.2. Create and use an enumeration type.
3. Declare a structure type.
4. Create and use a structure type.
5. Explain the differences in behavior between a structure and a class.
Declaring an enumeration
enum Season { Spring, Summer, Fall, Winter }
Using an enumeration
You can assign a value that is defined by the enumeration only to an enumeration variable.
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;
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.
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, “Using Indexers”). 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.
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).
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.
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.
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).
Working with Structures
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
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.
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.
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.A better idea is to make the fields private and provide your structure with constructors and methods
to initialize and manipulate these fields。
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, “Operator Overloading.”
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
You can’t declare a default constructor (a constructor with no parameters) for a structure.
The reason you can’t declare your own default constructor for a structure is that the compiler always generates one.
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.
In a class, you can initialize instance fields at their point of declaration. In a structure, you cannot.
There are other differences between classes and structures concerning inheritance. These differences are covered in Chapter 12, “Working with Inheritance.”
Declaring Structure Variables
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;
Time? currentTime = null;
Understanding Structure Initialization
Time now = new Time();
However, because structures are value types, you can also create structure variables without calling a constructor
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.
Time now = new Time(12, 30);
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).
Date now = new Date();
Date copy = now;
Date copy = now;
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.
Note C++ programmers should note that this copy behavior cannot be customized.
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。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 the managed execution environment, and C# programs are frequentlyreferred to as managed code.
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 the managed execution environment, and C# programs are frequentlyreferred to as managed code.
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.
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, “Creating Interfaces and Defining Abstract Classes” provides more information.
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, “Creating Interfaces and Defining Abstract Classes” provides more information.