The data types you've learned about so far hold strings, integers, dates, and other predefined kinds of information, but sometimes it would be nice to define your own data types.
An enumeration (or enumerated type) lets you define a new data type that can take only one of an allowed list of values. For example, a menu program might define a MealType
data type that can hold the values Breakfast
, Lunch
, and Dinner
.
The data types described in previous lessons also can hold only a single piece of data: a name, street address, city, or whatever. Sometimes it would be nice to keep related pieces of data together. Instead of storing a name, address, and city in separate strings, you might like to store them as a single unit.
A structure (sometimes called a struct) lets you define a group of related pieces of data that should be kept together.
In this lesson, you learn how to define and use enumerations and structures to make your code easier to write, understand, and debug.
Defining an enumeration is easy. The following code defines a ContactMethod
enumeration that can hold the values None
, Email
, Phone
, or SnailMail
:
// Define possible contact methods.
enum ContactMethod
{
None = 0,
Email,
Phone,
SnailMail,
}
Internally an enumeration is stored as an integral data type, by default an int
. An optional number after a value tells C# explicitly which integer to assign to that value. In the preceding code, None
is explicitly assigned the value 0.
If you don't specify a value for an enumeration's item (and often you don't care what these values are), its value is one greater than the previous item's value (the first item gets value 0). In this example, None
is 0, Email
is 1, Phone
is 2, and SnailMail
is 3.
You create an instance of an enumerated type just as you make an instance of a primitive type such as int
, decimal
, or string
. The following code declares a variable of type ContactMethod
, assigns it the value ContactMethod.Email
, and then displays its value in the Output window:
ContactMethod contactMethod = ContactMethod.Email;
Console.WriteLine(contactMethod.ToString());
An enumeration's ToString
method returns the value's name, in this case “Email.”
Defining a structure is just as easy as defining an enumeration. The following code defines a simple structure named Address
that holds name and address information:
// Define a structure to hold addresses.
struct Address
{
public string Name;
public string Street;
public string City;
public string State;
public string Zip;
public string Email;
public string Phone;
public ContactMethod PreferredMethod;
}
Inside the braces, the structure defines the bits of data that it holds together. The public
keywords in this example mean that the fields inside the structure (Name, Street, and so on) are visible to any code that can see an Address
.
Notice that the structure can use an enumeration. In this example, the Address
structure's PreferredMethod
field has type ContactMethod
.
In many ways structures behave like simple built-in types such as int
and float
. In particular, when you declare a variable with a structure type, the code not only declares it but also creates it. That means you don't need to use the new
keyword to create an instance of a structure.
After defining the variable, you can access its fields using syntax similar to the way you access a control's properties. Start with the variable's name, follow it with a dot, and then add the field's name.
The following code creates and initializes a new Address
structure named homeAddress
:
Address homeAddress;
homeAddress.Name = nameTextBox.Text;
homeAddress.Street = streetTextBox.Text;
homeAddress.City = cityTextBox.Text;
homeAddress.State = stateTextBox.Text;
homeAddress.Zip = zipTextBox.Text;
homeAddress.Email = emailTextBox.Text;
homeAddress.Phone = phoneTextBox.Text;
homeAddress.PreferredMethod =
(ContactMethod)preferredMethodComboBox.SelectedIndex;
This code fills in the text fields using values entered by the user in TextBox
es.
The final field is a ContactMethod
enumeration. The user selects a value for this field from the preferredMethodComboBox
. The code takes the index of the ComboBox
's selected item, converts it from an integer into a ContactMethod
, and saves the result in the structure's PreferredMethod
field.
In many ways structures are very similar to classes. Lesson 23 says a lot more about classes and the sorts of things you can do with them, and many of the same techniques apply to structures.
For example, both can contain properties, methods, and events. Both can also have constructors, special methods that are executed when you use new
to create a new instance. These are described in greater detail in Lesson 23.
While structures and classes have many things in common, they also have some significant differences. A lot of these differences are outside the scope of this book, so I won't cover them here, but one very important difference that you should understand is that structures are value types while classes are reference types.
A reference type doesn't actually hold the data for a class instance. Instead it holds a reference to an instance. The reference is like an address that points to where the data is actually stored.
For example, the following code creates a NewUserForm
and displays it:
NewUserForm userForm;
userForm = new NewUserForm();
userForm.ShowDialog();
The first statement declares a variable of type NewUserForm
. Initially that variable doesn't refer to anything so if you tried to display the form at this point, the program would crash.
The second statement creates a new instance of the NewUserForm
type and saves a reference to the new form in the userForm
variable.
Now the variable refers to an instance of the NewUserForm
type, so the third statement can safely display that form.
In contrast to reference types, a value type actually contains its data instead of refers to it. Many of the primitive data types such as int
, double
, and decimal
are value types.
The following code creates and uses a variable with the Address
structure type described earlier:
Address homeAddress;
homeAddress.Name = "Benjamin";
When the code executes the first statement, the program creates the Address
structure so it's all ready to go, although its fields all contain null
values. The second statement can immediately set the variable's Name
value without needing to use the new
keyword to create a new instance of the structure.
Another important difference between value and reference types involves the way the program assigns values to them.
If a program sets one reference variable equal to another, then they both point to the same object. For example, suppose ann
and ben
are two variables that hold references to Student
objects. Then the statement ben = ann
makes the variable ben
refer to the same object to which ann
refers.
Figure 17.1 shows this operation graphically. Initially (the picture on the left) variable ann
contains a reference to a Student
object and variable ben
contains the special value null
(represented by the box with an X in it) that means it doesn't refer to anything. After executing the statement ben = ann
, both variables contain references to the same Student
object (the picture on the right).
Because the two reference variables refer to the same object, if you use one variable to change the object, the other variable also sees the change. For example, if you execute the statement ben.FirstName = "Ben"
, then the value ann.FirstName
will also contain the value Ben
.
In contrast, if you set a variable with a value type equal to another, the first variable receives a copy of the second variable's value. For example, suppose cindy
and dan
are two variables of the structure type Person
. The Person
type might be very similar to the Student
type, except it's a structure (value type) instead of a class (reference type). In that case, the statement dan = cindy
makes the variable dan
hold a copy of the values in the structure cindy
.
Figure 17.2 shows this operation graphically. Initially (the picture on the left) variables cindy
and dan
each contain Person
data. This time the variables include all of the data inside the rectangles; they're not just references pointing to values stored someplace else. After executing the statement dan = cindy
, both variables contain separate copies of the same data.
Because the two value variables refer to different copies of the same data, changing one doesn't change the other. For example, if you execute the statement dan.Name = "Dan"
, then the value cindy.Name
will still be Cindy
.
The Structure Versus Class example program, which is available in this lesson's downloads, demonstrates this difference. This issue is quite important, so it will be worth your time to download the example and study it until you're sure you understand it.
So which should you use, a structure or a class? In many programs the difference doesn't matter much. As long as you are aware of the relevant differences, you can often use either.
Microsoft's “Classes and Structs (C# Programming Guide)” web page at msdn.microsoft.com/library/ms173109.aspx
gives this advice:
In general, classes are used to model more complex behavior, or data that is intended to be modified after a class object is created. Structs are best suited for small data structures that contain primarily data that is not intended to be modified after the struct is created.
If you follow that advice, then a more complex piece of data such as a Person
or Student
should probably be implemented as a class. You may need to update Person
or Student
information over time, so that also indicates that these should probably be classes.
In contrast, suppose you're writing an oven control program and you want a data type to store temperature data. In that case, you might store data in a Temperature
structure.
On some level, it doesn't make sense for a particular temperature value to change, although it might make sense for an oven's temperature to change. For example, the oven's temperature might start at 75° and warm up to 375°. The temperature 75° hasn't changed; it's the oven's temperature that has changed. Instead of updating the temperature variable, the program would set the variable equal to the new temperature value.
To see the difference, think back to the Student
example. If Ann moves, you'll need to change her address (assuming the Student
class contains name, address, phone number, and other relevant data). Ann herself hasn't changed, so it doesn't really make sense to set the ann
variable equal to a whole new Student
object. Instead you can just update the ann.Address
value.
If you think I'm just being nit-picky and splitting hairs here, you're right. The difference is there, but for practical purposes it often doesn't make a huge difference whether you use a class or a structure. A lot of C# programmers use classes instead of structures basically all of the time. (Partly I suspect so they don't have to remember the differences between value and reference types.) If you're using classes and structures defined by Microsoft or some other programmer, then the differences matter, but when you're writing your own code, you can pick whichever makes the most sense to you.
You can define structures in a couple places.
First, you can define a structure inside a class but outside of any of its methods. For example, you can define a structure inside a form class. Then the structure is visible only inside the class that contains it. If code outside of the class doesn't need to use the structure, this restricts the structure's visibility so it prevents possible confusion in the outside code.
Second, you can define a structure in the file that defines a class but outside of the class's code. For example, you can put it at the bottom of the class just before the final closing brace that ends the namespace
statement started at the top of the file. In that case, the structure is visible to all of the code in the project (assuming you give it enough visibility, for example, public
).
The second method can be a bit confusing because the same file defines a class and a structure. A third place you can define a structure for use by the whole program is in its own module. The easiest way to do that is to use the Project menu's Add Class command. Give the class the name you want to give the structure and click Add. After Visual Studio creates the class, change the class
keyword to struct
.
You can define enumerations in the same locations.
In this Try It, you use an enumeration and a structure to make the address book shown in Figure 17.3. When the user clicks the Add button, the program saves the entered address values. If the user enters a name and clicks Find, the program retrieves the corresponding address data.
In this lesson, you:
ContactMethod
enumeration with values None
, Email
, Phone
, and SnailMail
.Address
structure to hold the entered address information.Dictionary<string, Address>
field to hold the address data.ComboBox
's None
entry when the form loads (just so something is selected).Dictionary
.Dictionary
and displays it.
ContactMethod
enumeration with values None
, Email
, Phone
, and SnailMail
.
// Define contact methods.
private enum ContactMethod
{
None,
Email,
Phone,
SnailMail,
}
Address
structure to hold the entered address information.
// Define the address structure.
private struct Address
{
public string Name;
public string Street;
public string City;
public string State;
public string Zip;
public string Email;
public string Phone;
public ContactMethod PreferredMethod;
}
Dictionary<string, Address>
field to hold the address data.
// Make a Dictionary to hold addresses.
private Dictionary<string, Address> Addresses =
new Dictionary<string, Address>();
ComboBox
's None
entry when the form loads.
// Make sure the ComboBox starts with an item selected.
private void Form1_Load(object sender, EventArgs e)
{
preferredMethodComboBox.SelectedIndex = 0;
}
Dictionary
.
Dictionary
's Add
method lets the Add button add or update a record.) Optionally you can clear the TextBox
es to get ready for the next address.// Add a new address.
private void addButton_Click(object sender, EventArgs e)
{
// Fill in a new Address structure.
Address newAddress;
newAddress.Name = nameTextBox.Text;
newAddress.Street = streetTextBox.Text;
newAddress.City = cityTextBox.Text;
newAddress.State = stateTextBox.Text;
newAddress.Zip = zipTextBox.Text;
newAddress.Email = emailTextBox.Text;
newAddress.Phone = phoneTextBox.Text;
newAddress.PreferredMethod =
(ContactMethod)preferredMethodComboBox.SelectedIndex;
// Add the name and address to the dictionary.
Addresses[nameTextBox.Text] = newAddress;
// Get ready for the next one.
nameTextBox.Clear();
streetTextBox.Clear();
cityTextBox.Clear();
stateTextBox.Clear();
zipTextBox.Clear();
emailTextBox.Clear();
phoneTextBox.Clear();
preferredMethodComboBox.SelectedIndex = 0;
nameTextBox.Focus();
}
Dictionary
and displays it.
// Look up an address.
private void findButton_Click(object sender, EventArgs e)
{
// Get the Address.
Address selectedAddress = Addresses[nameTextBox.Text];
// Display the Address's values.
nameTextBox.Text = selectedAddress.Name;
streetTextBox.Text = selectedAddress.Street;
cityTextBox.Text = selectedAddress.City;
stateTextBox.Text = selectedAddress.State;
zipTextBox.Text = selectedAddress.Zip;
emailTextBox.Text = selectedAddress.Email;
phoneTextBox.Text = selectedAddress.Phone;
preferredMethodComboBox.SelectedIndex =
(int)selectedAddress.PreferredMethod;
}
Dictionary
's Remove
method.Address
structure so it contains an array holding three phone numbers: home, work, and cell. (Hint: Before you can store values in the array, you need to allocate it as in theAddress.Phones = new string[3]
.)Make a program that creates an array holding five Address
structures of the kind used by the program you wrote for Exercise 2. When the program starts, initialize the array to literal values hardcoded into the program. Place the structures' names in a ComboBox
. When the user selects an entry from the ComboBox
, display the corresponding data.
string
and an int
in a TextBox
.Suppose you're opening a coffee shop and you want to have the sizes Grande, Enorme, and Demente. Because some customers will be too grouchy to use the fancy names (because they haven't had their coffee yet), those names should be equivalent to the more pedestrian names Big, Huge, and Ginormous. Make a program that creates an enumeration that defines all of those values. Then display each value as both a string
and an int
in a TextBox
.
(Hint: Because a structure cannot contain direct instances of itself, you'll need to figure out a way to store the parents in a reference type.)
3.129.218.45