Team LiB   Previous Section   Next Section

13.1 Defining a Struct

The syntax for declaring a struct is almost identical to that for a class:

[attributes] [access-modifiers] struct identifier [:interface-list]
{ struct-members }

Attributes are not discussed in this book. [Refer to Programming C#, Second Edition (O'Reilly), for more information about attributes.] Access modifiers (public, private, etc.) work just as they do with classes. (See Chapter 8 for a discussion of access modifiers.) The keyword struct is followed by an identifier (the name of the struct). The optional interface list is explained in Chapter 14. Within the braces is the body of the struct (fields and methods, also called the struct members).

Example 13-1 defines a struct named Location to hold the x,y coordinates of an object displayed on the screen and demonstrates that structs are value objects. When you pass a struct to a method, you pass it by value.

To create this application, open a console application in Visual Studio .NET and name it StructDemonstration.

Example 13-1. Creating a struct for x,y coordinate location
using System;

namespace StructDemonstration
{
    // declare a struct named Location
    public struct Location
    {
        // the struct has private data
        private int xVal;
        private int yVal;

        // constructor
        public Location(int xCoordinate, int yCoordinate)
        {
            xVal = xCoordinate;
            yVal = yCoordinate;
        }

        // property
        public int XVal
        {
            get { return xVal; }
            set { xVal = value;}
        }

        public int YVal
        {
            get { return yVal; }
            set { yVal = value; }
        }

        // Display the Struct as a String
        public override string ToString()
        {
            return (String.Format("{0}, {1}", xVal,yVal));
        }
    }       // end struct

    public class Tester
    {
        public void Run()
        {
            // create an instance of the struct
            Location loc1 = new Location(200,300);

            // display the values in the struct
            Console.WriteLine("Loc1 location: {0}", loc1);

            // invoke the default constructor
            Location loc2 = new Location();          
            Console.WriteLine("Loc2 location: {0}", loc2);

            // pass the struct to a method
            myFunc(loc1);

            // redisplay the values in the struct
            Console.WriteLine("Loc1 location: {0}", loc1);
        }

        // method takes a struct as a parameter
        public void myFunc(Location loc)
        {
            // modify the values through the properties
            loc.XVal = 50;
            loc.YVal = 100;
            Console.WriteLine("Loc1 location: {0}", loc);
        }

        static void Main()
        {
            Tester t = new Tester();
            t.Run();
        }
    }
}


Output:
Loc1 location: 200, 300
Loc2 location: 0, 0
Loc1 location: 50, 100
Loc1 location: 200, 300

The Location structure is defined as public, much as you might define a class:

public struct Location
{

As with a class, you can define a constructor and properties for the struct. For example, you might create private int member fields xVal and yVal and then provide public properties for them named XVal and YVal (see Chapter 9):

// constructor
public Location(int xCoordinate, int yCoordinate)
{
    xVal = xCoordinate;
    yVal = yCoordinate;
}

// property
public int XVal
{
    get { return xVal; }
    set { xVal = value;}
}

public int YVal
{
    get { return yVal; }
    set { yVal = value; }
}

Note that there is one significant difference in the way you create constructors and properties for structs and the way you do it for classes: in a struct, you are not permitted to create a custom default constructor. That is, you cannot write a constructor with no parameters. Thus the following code would not compile:

// won't compile - no custom default
// constructors for structs
public Location()
{
    xVal = 5;
    yVal = 10;
}

Instead, the compiler creates a default constructor for you (whether or not you create other constructors) and that default constructor initializes all the member values to their default values (e.g., ints are initialized to zero).

The Run() method creates an instance of the Location struct named loc1, passing in the initial x,y coordinates of 200,300.

Location loc1 = new Location(200,300);

The value is then displayed, passing loc1 to WriteLine().

Console.WriteLine("Loc1 location: {0}", loc1);

As always, when you pass an object to Console.WriteLine() (in this case loc1), WriteLine() automatically invokes the virtual method ToString() on the object. Thus, Location.ToString() is invoked, which displays the x,y coordinates of the loc1 object.

Loc1 location: 200, 300

Before modifying the values in loc1, the example creates a second instance of the Location struct, named loc2. The creation of loc2 invokes the default constructor (note that there are no parameters passed in).

Location loc2 = new Location();

The output shows that the compiler-provided default constructor initialized the member variables to default values.

Loc2 location: 0, 0

Notice that you have not provided a default constructor; instead one has been provided for you by the compiler.

Next pass your first struct, loc1 (whose values are 200,300), to a method, myFunc(). In that method, the parameter is a Location object named loc. Within the myFunc() method, the XVal property is used to set the x coordinate to 50, and the YVal property is used to set the y coordinate to 100; then the new value is displayed using WriteLine():

public void myFunc(Location loc)
{
    // modify the values through the properties
    loc.XVal = 50;  // set XVal property
    loc.YVal = 100; // set YVal property
    Console.WriteLine("Loc1 location: {0}", loc);
}

As expected, the results show the modification:

Loc1 location: 50, 100

When you return to the calling method (Run()), the values of loc1 are displayed, and they are unchanged from before the call to myFunc():

Loc1 location: 200, 300

When you passed loc1 to myFunc(), the struct was passed by value (structs, like the intrinsic types, are value types). A copy was made, and it was on that copy that you changed the values to 50 and 100. The original Location structure (loc1) was unaffected by the changes made within myFunc().

13.1.1 No Inheritance

Unlike classes, structs do not support inheritance. Structs implicitly derive from Object (as do all types in C#, including the built-in types) but cannot inherit from any other class or struct. Structs are also implicitly sealed (that is, no class or struct can derive from a struct). See Chapter 11 for a discussion of inheritance and sealed classes.

13.1.2 No Initialization

You cannot initialize fields in a struct. Thus, it is illegal to write:

private int xVal = 50;
private int yVal = 100;

though this kind of initialization is perfectly legal in a class. You must instead set the value of your member fields in the body of the constructor. As noted earlier, the default constructor (provided by the compiler) sets all the member variables to their default value.

13.1.3 Public Member Data?

Structs are designed to be simple and lightweight. While private member data promotes data hiding and encapsulation, some programmers feel it is overkill for structs. They make the member data public, thus simplifying the implementation of the struct. Other programmers feel that properties provide a clean and simple interface, and that good programming practice demands data hiding even with simple lightweight objects. Which you choose is a matter of design philosophy; the language will support either approach.

    Team LiB   Previous Section   Next Section