The C# Station Tutorial
by Joe Mayo, 01/19/02, updated 3/12/03, 2/22/08, 4/29/08, 1/12/09, 5/6/09
Lesson 12: Structs
This lesson will teach you about the C# struct. Our objectives are as follows:
- Understand the Purpose of structs.
- Implement a struct.
- Use a struct.
What is a struct?
A struct is a value type. To help understand the struct, it's helpful to
make a comparison with classes, as described in Lesson
7: Introduction to Classes and subsequent chapters. While a struct is
a value type, a class is a reference type. Value types hold their value in
memory where they are declared, but reference types hold a reference to an
object in memory. If you copy a struct, C# creates a new copy of
the object and assigns the copy of the object to a separate struct
instance. However, if you copy a class, C# creates a new copy of the reference to the
object and assigns the copy of the reference to the separate class
instance. Structs can't
have destructors, but classes can have destructors. Another difference between a
struct and class is that a struct can't have implementation inheritance, but a
class can, as described in Lesson 8: Class Inheritance.
Although a struct can't have implementation inheritance, it can have
interface
inheritance, as described in Lesson 13: Interfaces,
which is the next lesson following this one. Lesson 22:
Topics on C# Type, digs deeper into the differences between value and
reference types, providing code that demonstrates the concepts that are
introduced here.
The .NET Framework includes many types that are structs, including many of the
built-in types. For example, a System.Int32 is a C# int, a
System.Single
is a C# float, and a System.Bool is a C# bool. The C#
built-in types are aliases for .NET Framework types, giving you
language-specific syntax. If you look at the
documentation for any of these .NET Framework types, you'll see them declared as
struct types. That means you'll need to recognize what a struct type is when you
see it, which the next section helps with by showing you how to create your own
custom struct type.
Creating a Custom struct Type
While the behavior of class and struct types are very different, their syntax is
similar. You declare the type and its members with the primary visual difference
being that a struct uses the keyword struct and a class uses the keyword
class.
The example in Listing 12-1 demonstrates how to define a custom struct. In this
case, the struct is a Rectangle with Width and Height properties, similar to
what you might use to represent a rectangular shape on a screen.
Listing 12-1. Defining a struct
/// <summary>
/// Custom struct type, representing a rectangular shape
/// </summary>
struct Rectangle
{
/// <summary>
/// Backing Store for Width
/// </summary>
private int m_width;
/// <summary>
/// Width of rectangle
/// </summary>
public int Width
{
get
{
return m_width;
}
set
{
m_width = value;
}
}
/// <summary>
/// Backing store for Height
/// </summary>
private int m_height;
/// <summary>
/// Height of rectangle
/// </summary>
public int Height
{
get
{
return m_height;
}
set
{
m_height = value;
}
}
}
As you can see, the Rectangle struct in Listing 1-2 looks very much like a class
with a couple properties, except that it uses the keyword struct, instead of the
keyword class, to declare that Rectangle is a struct.
Using a struct
To use a struct, instantiate the struct and use it just like a
class.
Listing 12-2 shows how to instantiate the Rectangle struct and access its
properties.
Listing 12-2. Using a Struct
using System;
/// <summary>
/// Example of declaring and using a struct
/// </summary>
class StructExample
{
/// <summary>
/// Entry point: execution starts here
/// </summary>
static void Main()
{
// instantiate a new Rectangle struct
// where Width is set to 1 and Height is set to 3
Rectangle rect1 = new Rectangle();
rect1.Width = 1;
rect1.Height = 3;
// show the value of Width and Height for rect1
Console.WriteLine("rect1: {0}:{1}", rect1.Width, rect1.Height);
Console.ReadKey();
}
}
The code in the Main method of Listing 12-2 instantiates a new Rectangle
struct
and sets its Height and Width properties. The experience is similar to how a
class can be used. Here's the output:
rect1: 1:3
An alternate way of instantiating a struct and setting its properties is with an
object initializer, shown below:
// you can also use object initialization syntax
Rectangle rect11 = new Rectangle
{
Width = 1,
Height = 3
};
Notice that the object initializer uses curly braces and sets properties via a
comma-separated list of name/value pairs.
Overloading struct Constructors
The two previous examples of instantiating a struct, via constructor only and
via object initializer, used the default (parameterless) constructor of the struct. The default constructor is implicitly defined by C# and you can't
implement the default constructor yourself. The default constructor
initializes all struct fields to default values. i.e. integrals are 0, floating
points are 0.0, and booleans are false. If you need custom constructor
overloads, you can add new constructors, as long as they have one or more
parameters. Listing 12-3 shows a customization of the Rectangle struct from
Listing 12-1 that includes a constructor overload.
Listing 12-3: Overloading a struct Constructor
/// <summary>
/// Custom struct type, representing a rectangular shape
/// </summary>
struct Rectangle
{
/// <summary>
/// Backing Store for Width
/// </summary>
private int m_width;
/// <summary>
/// Width of rectangle
/// </summary>
public int Width
{
get
{
return m_width;
}
set
{
m_width = value;
}
}
/// <summary>
/// Backing store for Height
/// </summary>
private int m_height;
/// <summary>
/// Height of rectangle
/// </summary>
public int Height
{
get
{
return m_height;
}
set
{
m_height = value;
}
}
/// <summary>
/// Instantiate rectangle struct with dimensions
/// </summary>
/// <param name="width">Width to make new rectangle</param>
/// <param name="height">Height to make new rectangle</param>
public Rectangle(int width, int height)
{
m_width = width;
m_height = height;
}
}
The highlighted portion of code in Listing 12-3 is a constructor overload.
Constructors are named the same as their containing class, which is Rectangle in
this case. This Rectangle constructor overload has two parameters, which it
assigns to backing stores that are encapsulated by properties for calling code.
Listing 12-4 shows an example of how you would use the constructor overload in
Listing 12-3 to instantiate a new Rectangle.
Listing 12-4: Instantiating a struct Through a Constructor Overload
using System;
/// <summary>
/// Example of declaring and using a struct
/// </summary>
class StructExample
{
/// <summary>
/// Entry point: execution starts here
/// </summary>
static void Main()
{
// instantiate a new Rectangle struct
// where Width is set to 5 and Height is set to 7
Rectangle rect2 = new Rectangle(5, 7);
// show the value of Width and Height for rect2
Console.WriteLine("rect2: {0}:{1}", rect2.Width, rect2.Height);
Console.ReadKey();
}
}
The code in the Main method of Listing 12-4 instantiates a Rectangle
struct and
displays the values set via the constructor overload. When instantiating rect2,
the code passes the values 5 and 7 as arguments. From Listing 12-3, you can see
that the Width of rect2 will be set to 5 and the Height of
rect2 will be set to
7. Here's the output from Listing 12-4:
rect2: 5:7
Adding a Method to a struct
All of the examples so far showed how you can add properties and constructors to
a struct, but you can also add methods to a struct. Defining a method in a
struct is the same as defining a method in a class. Listing 12-5 shows the
Rectangle struct with a method named Add.
Listing 12-5: Adding a Method to a struct
/// <summary>
/// Custom struct type, representing a rectangular shape
/// </summary>
struct Rectangle
{
/// <summary>
/// Backing Store for Width
/// </summary>
private int m_width;
/// <summary>
/// Width of rectangle
/// </summary>
public int Width
{
get
{
return m_width;
}
set
{
m_width = value;
}
}
/// <summary>
/// Backing store for Height
/// </summary>
private int m_height;
/// <summary>
/// Height of rectangle
/// </summary>
public int Height
{
get
{
return m_height;
}
set
{
m_height = value;
}
}
/// <summary>
/// Instantiate rectangle struct with dimensions
/// </summary>
/// <param name="width">Width to make new rectangle</param>
/// <param name="height">Height to make new rectangle</param>
public Rectangle(int width, int height)
{
m_width = width;
m_height = height;
}
/// <summary>
/// Increase the size of this rectangle by the size of the specified rectangle
/// </summary>
/// <param name="rect">Rectangle that will be added to this rectangle</param>
/// <returns>New rectangle created by adding rect to this rectangle</returns>
public Rectangle Add(Rectangle rect)
{
// create instance of rectangle struct with default constructor
Rectangle newRect = new Rectangle();
// add matching axes and assign to new Point struct
newRect.Width = Width + rect.Width;
newRect.Height = Height + rect.Height;
// return new point struct
return newRect;
}
}
The highlighted code in Listing 12-5 is a method named Add. It might or
might not make sense to add two Rectangle structs together, but the example
demonstrates how to define a method in a struct. In this case, the Add method
will increase the Height and Width of the current Rectangle instance by adding
the Height and Width in the rect parameter. The result of the method is a new
Rectangle with the added properties.
Calling a struct Method
You can call the Add method, from Listing 12-5, through an instance of a
Rectangle struct. Listing 12-6 shows how to instantiate two Rectangle
structs,
call the Add method and assign the result of the Add method call to another
Rectangle struct.
Listing 12-5: Calling a struct Method
using System;
/// <summary>
/// Example of declaring and using a struct
/// </summary>
class StructExample
{
/// <summary>
/// Entry point: execution starts here
/// </summary>
static void Main()
{
// instantiate a new Rectangle struct
// where Width is set to 1 and Height is set to 3
Rectangle rect1 = new Rectangle();
rect1.Width = 1;
rect1.Height = 3;
// show the value of Width and Height for rect1
Console.WriteLine("rect1: {0}:{1}", rect1.Width, rect1.Height);
// instantiate a new Rectangle struct
// where Width is set to 5 and Height is set to 7
Rectangle rect2 = new Rectangle(5, 7);
// show the value of Width and Height for rect2
Console.WriteLine("rect2: {0}:{1}", rect2.Width, rect2.Height);
// invoke the Add method on the rect1 Rectangle struct instance,
// passing the rect2 Rectangle struct instance as an argument
// and assigning the new copy of the value returned by the
// Add method to the rect3 Rectangle struct.
Rectangle rect3 = rect1.Add(rect2);
// show the value of Width and Height for rect3
Console.WriteLine("rect3: {0}:{1}", rect3.Width, rect3.Height);
Console.ReadKey();
}
}
In the Main method of Listing 12-5, the code instantiates rect1 and
rect2, which
are both Rectangle structs, assigning values to their Height and
Width
properties. The struct instantiation examples should be familiar by now because
they are the same as earlier examples. What's useful about Listing 12-5 is the
highlighted code, which shows how to invoke the Add method of the Rectangle
struct. The code invokes the Add method of the rect1 instance and passes
rect2
as the Rectangle struct to be added to rect1. The Add method in Listing 12-4
shows what happens when this code executes. In Listing 12-5, the return
value of the Add method is assigned to rect3, which is a larger
Rectangle with
each of its sides equal to the sum of the individual sides of rect1 and
rect2.
Here's the output:
rect1: 1:3
rect2: 5:7
rect3: 6:10
Summary
This lesson described what a struct was and identified a few differences between
class and struct types. You learned how to create a struct. You can
instantiate a struct either via a default constructor or a custom constructor
overload that you write. You also saw how to implement properties and methods in
structs.
I invite you to return for Lesson 13: Interfaces.
Your feedback and constructive contributions are welcome. Please feel free
to contact me for feedback or comments you may have about this lesson.
Copyright © 2000-2010 C# Station, All Rights Reserved