C# Struct

C# Struct is a lightweight type to represent an object comparing with C# class. The syntax to define a struct is as follows.

[public|internal|] struct <struct name>
{
    // Fields, properties and methods go here...
}

Comparing with a class, a struct has the following limitations.

  • In a struct declaration, the fields except const or static ones cannot be initialized.
  • A default constructor or a destructor is not allowed to be declared in a struct but the constructors with parameters are allowed.
  • Structs are defined as value types but classes are defined as reference types.
  • Unlike a class, the new operator is not necessary to instantiate a struct.
  • A struct can implement interfaces but cannot inherit from another struct or class.
  • All structs inherit directly from the abstract class System.ValueType which derives from System.Object.
  • All the built-in data types except string and object are value types and defined as structs.

Example 01-73-01

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
using System;

struct S
{
    int x;

    public S(int x)
    {
        this.x = x;
    }

    public void output()
    {
        Console.WriteLine("x={0}",x);
    }
}

class B
{
    protected int x = 100;

    public void output()
    {
        Console.WriteLine("x={0}", x);
    }
}

class A : B
{
    public A(int x)
    {
        this.x = x;
    }

    public A()
    {
        this.x = -1;
    }
}

class Program
{
    static void Main()
    {
        A a = new A();
        a.output();

        S s = new S(5);
        s.output();

        s = new S();
        s.output();

        Console.Read();
    }
}

Output

x=-1
x=5
x=0

Explanation

  • Line 3-16: Define a struct S.
  • Line 5: x cannot be initialized.
  • Line 7-10: The default constructor cannot be defined but the constructor with parameters is allowed.
  • Line 18-39: Define class A inheriting from class B.
  • Line 20: 100 is assigned to x when declaring the field x.
  • Line 22-25: Define the same method output() but it can be inherited in class A.
  • Line 35-38: Default constructor is declared here.
  • Line 51: The default constructor of the struct created by CLR will be called and it automatically initializes all the fields of the object with their default values of the types.

No matter how you created an object of a struct, you will have to initialize all the fields of the struct before using it. If new operator is used to create a struct object, the constructors will take responsibility to initialize the fields. The default constructor cannot be created in the program but it can be created automatically by CLR. It will initialize all the fields with the default values of the field types.

Example 01-73-02

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
using System;

struct Circle
{
    public double radius;
    const double PI = 3.14;

    public void area()
    {
        Console.WriteLine("Area: {0}", radius*radius*PI);
    }
}

class Program
{
    static void Main()
    {
        Circle c;
        c.radius = 10;
        c.area();

        Console.Read();
    }
}

Output

Area: 314

Explanation

  • Line 3-12: Define a struct Circle.
  • Line 5: radius cannot be initialized.
  • Line 6: const or static field is allowed to be initialized.
  • Line 8-11: Output the area of the circle.
  • Line 18: Declare a circle c without using new operator.
  • Line 19: The only field radius is assigned before using c.
  • Line 20: Now the method can be called because all the fields of the object c were already initialized.

Example 01-73-03

The following is an example to compare the benchmarks of running speed between structs and classes.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
using System;

struct CircleA
{
    public double radius;
    const double PI = 3.1416;

    public double area()
    {
        return radius * radius * PI;
    }
}

class CircleB
{
    public double radius;
    const double PI = 3.1416;

    public double area()
    {
        return radius * radius * PI;
    }
}

class Program
{
    const long LOOPs = 100000000;

    static void Main()
    {
        DateTime d1, d2, d3, d4;

        d1 = DateTime.Now;
        for (long i = 0; i < LOOPs; i++)
        {
            CircleB a = new CircleB();
            a.radius = i;
            double theArea = a.area();
        }

        d2 = DateTime.Now;
        for (long i = 0; i < LOOPs; i++)
        {
            CircleA a = new CircleA();
            a.radius = i;
            double theArea = a.area();
        }

        d3 = DateTime.Now;
        for (long i = 0; i < LOOPs; i++)
        {
            CircleA a;
            a.radius = i;
            double theArea = a.area();
        }

        d4 = DateTime.Now;
        Console.WriteLine(d2.Subtract(d1));
        Console.WriteLine(d3.Subtract(d2));
        Console.WriteLine(d4.Subtract(d3));
        Console.Read();
    }
}

Output

00:00:02.0962096
00:00:01.9431943
00:00:01.9411941

Explanation

  • Line 3-23: Define a struct CircleA and a class CircleB with the exactly same members.
  • Line 31: Declare 4 DateTime variables to record the timestamps.
  • Line 33: Get the right now timestamp.
  • Line 34-39, 42-47, 51-55: This is the same process in the loop.
  • Line 36: Create an object of class CircleB .
  • Line 44: Declare struct CircleA variable with new operator.
  • Line 52: Declare struct CircleA variable without new operator.
  • Line 58-60: Get eclipsed time of each loop.

In summary of the above example, comparing with creating an object of a class, creating an object of a struct saves more time. Also, creating a struct object without new operator does not save too much time by comparing with that using new operator.