Structures


Classes are reference types and consequently they are accessed through a reference. Value types differ in that they are accessed directly. A structure is a value type and its presence is denoted by the struct keyword. Structures are syntactically similar to classes. The general form of the declaration of a structure is as follows.

struct name : interfaces
{
 // ... member declarations
}

The name of the structure is name and interfaces is a comma separated list of interface names. Structures cannot inherit from structures or classes or be used as a base class. Like all C# types, structures do however inherit from the implicit base class object. Structures can include methods, fields, indexers, properties, operators and events. Structures can define constructors but not a destructor. A restriction on constructors is that a default (parameterless) constructor cannot be defined. The default constructor of a structure initializes all the fields to their default values. Because structures do not support inheritance, structure members cannot be declared as virtual, abstract or protected.

The version of Complex shown below illustrates a few points about the initialization and use of structures.

// Structure1 - Structures

using System;

public struct Complex
{
    public double x;
    public double y;

    public Complex(double x_set) { x = x_set; y = 0; }

    public Complex(double x_set, double y_set) { x = x_set; y = y_set; }

    public override string ToString()
    {
        string c;

        if (x != 0)
            c = x.ToString();
        else if (y == 0)
            return "0";
        else
            c = "";

        if (y == 1)
            return c + "+i";
        if (y == -1)
            return c + "-i";
        else if (y < 0)
            return c + y.ToString() + "i";
        else
            return c + "+" + y.ToString() + "i";
    }
}

class Program
{
    public static void Main()
    {
        Complex z1 = new Complex(1, 2);  // Constructor to initialize
        Complex z2 = new Complex();      // Default constructor
        Complex z3;                      // No constructor 

        Console.WriteLine("z1 = {0}", z1);    // OK, z1 fully initialized by constructor 
        Console.WriteLine("z2 = {0}", z2);    // z2 initialized to zeros
        // Console.WriteLine("z3 = {0}", z3); // This one is an error - z3 uninitialized

        z3.x = 2; z3.y = 4;
        Console.WriteLine("z3 = {0}", z3);    // Now OK - z3 initialized

        Complex z4 = z3;
        Console.WriteLine("z4 = {0}", z4);    // Shows how copying is done
    }
}

Firstly, three complex numbers are created: z1, z2 and z3. The number z1 is fully initialized through one of the constructors. The number z2 is initialized through the default constructor, which sets the real (x) and imaginary (y) components to zero. The third complex number z3 is uninitialized. Both z1 and z2 are immediately written out to the console (which is OK because they are both initialized). The commented line of code that follows fails to compile when uncommented because z3 cannot be used before it is initialized. Before a component of z3 is used, it must be initialized. For example, before z3.x is used it must be assigned a value and before z3.y is used it must be assigned a value. These values act independently of each other in this regard. Both the real and imaginary parts of z3 are then initialized and then z3 is written to the console.

Next, a complex number z4 is allocated and copied from z3. Note that no copy constructor has been defined for the class (although one could have been) and thus the generated copy constructor is used. The generated copy constructor does a memberwise copy of the fields of the structure. The resulting output verifies this, and it is shown below.

z1 = 1+2i
z2 = 0
z3 = 2+4i
z4 = 2+4i

It is worthy of noting that structures (value types) react to an assignment operation in a radically different manner to classes (reference types). When a reference type is assigned to another reference type, the target object is set to refer to the same object as the source of the assignment (and no change occurs to the contents of the objects). Conversely, the source and destination of an assignment of value types remain independent but the contents are copied from the source to the destination - a stark constrast to the behaviour of reference types.