You are currently viewing C# Structures

C# Structures

When it comes to programming in C#, developers often encounter the need to work with data types that represent a group of related values. In such cases, C# structures prove to be a valuable tool. In this article, we’ll explore C# structures, what they are, how they differ from classes, and when to use them. We’ll also provide hands-on examples to illustrate their usage in various scenarios.

What are C# Structures?

In C#, a structure is a value type that can encapsulate data and related functionality. Unlike classes, which are reference types, structures are stored on the stack rather than the heap. This distinction brings about certain advantages, such as improved performance and reduced memory overhead, making structures a suitable choice for specific scenarios.

Declaration and Initialization

Let’s start by looking at how structures are declared and initialized in C#. Consider the following example of a simple Point structure:

using System;

public struct Point
{
    public int X;
    public int Y;
}

public class Structures
{
    public static void Main(string[] args)
    {
        
        // Initializing a Point structure
        Point point = new Point { X = 1, Y = 2 };

        // Accessing structure members
        Console.WriteLine($"X: {point.X}, Y: {point.Y}");
        
    }
}

In this example, the Point structure encapsulates two integers, representing the coordinates of a point in a two-dimensional space. The Main method demonstrates how to create an instance of the structure and access its members.

Default Constructor

Structures have an implicit default constructor that initializes all the fields with their default values. In the case of our Point structure, the default constructor sets both X and Y to 0.

using System;

public struct Point
{
    public int X;
    public int Y;
}

public class Structures
{
    public static void Main(string[] args)
    {
        
        Point origin = new Point(); // origin.X and origin.Y are both 0
        
        // Accessing structure members
        Console.WriteLine($"X: {origin.X}, Y: {origin.Y}");

        // Modifying the x, y values
        origin.X = 64;
        origin.Y = 70;
        
        // Accessing structure members
        Console.WriteLine($"X: {origin.X}, Y: {origin.Y}");

    }
}

Differences Between Structures and Classes

While structures share similarities with classes, there are key differences that distinguish them. Structures are value types, meaning they are directly stored in the memory location where they are declared. On the other hand, classes are reference types, and their instances are stored on the heap, with references to them held on the stack.

Structure:

using System;

public struct Point
{
    public int X;
    public int Y;
}

public class Structures
{
    public static void Main(string[] args)
    {
        
        Point point1 = new Point { X = 1, Y = 2 };
        Point point2 = point1; // Creates a copy
        
        // Accessing point1 structure members
        Console.WriteLine($"Point1 {{X: {point1.X}, Y: {point1.Y}}}");
        
        // Accessing point2 structure members
        Console.WriteLine($"Point2 {{X: {point2.X}, Y: {point2.Y}}}");

        // Modifying point1 x, y values
        point1.X = 64;
        point1.Y = 70;
        
        // Accessing point1 structure members
        Console.WriteLine($"Point1 {{X: {point1.X}, Y: {point1.Y}}}");
        
        // Accessing point2 structure members
        Console.WriteLine($"Point2 {{X: {point2.X}, Y: {point2.Y}}}");
        
    }
}

Class:

using System;

public class Point
{
    public int X { get; set; }
    public int Y { get; set; }
}

public class Structures
{
    public static void Main(string[] args)
    {
        
        Point point1 = new Point { X = 10, Y = 20 };
        Point point2 = point1; // References the same instance
        
        // Accessing point1 class members
        Console.WriteLine($"Point1 {{X: {point1.X}, Y: {point1.Y}}}");
        
        // Accessing point2 class members
        Console.WriteLine($"Point2 {{X: {point2.X}, Y: {point2.Y}}}");

        // Modifying point1 x, y values
        point1.X = 64;
        point1.Y = 70;
        
        // Accessing point1 class members
        Console.WriteLine($"Point1 {{X: {point1.X}, Y: {point1.Y}}}");
        
        // Accessing point2 class members
        Console.WriteLine($"Point2 {{X: {point2.X}, Y: {point2.Y}}}");
        
    }
}

In the structure example, point2 is a copy of point1. Modifying point1 does not affect point2. In the class example, point2 refers to the same instance as point1. Changes to point1 will be reflected in point2.

When to Use Structures

Structures are particularly useful in scenarios where lightweight objects are needed, and the overhead of reference types is undesirable. Let’s explore scenarios where using structures is beneficial.

Performance Considerations

Structures are stored on the stack, making them more memory-efficient than classes, which are stored on the heap. This can be advantageous when working with small, lightweight objects, as it reduces memory overhead and enhances performance.

Consider a scenario where you need to represent a color as a combination of red, green, and blue components:

public struct RGBColor
{
    public byte Red;
    public byte Green;
    public byte Blue;
}

In this case, using a structure is more efficient than a class, especially when dealing with a large number of color instances.

Immutability

Structures are often used for creating immutable types. Immutability ensures that once a structure is created, its state cannot be modified. This can simplify code and reduce the chances of unintended side effects.

public struct ImmutableRectangle
{
    public readonly int Width;
    public readonly int Height;

    public ImmutableRectangle(int width, int height)
    {
        Width = width;
        Height = height;
    }
}

In the above example, the ImmutableRectangle structure is immutable, and its dimensions cannot be changed once it is created.

Working with C# Structures

Now that we understand the basics, let’s explore some practical examples to illustrate how structures can be effectively used in C#.

Point in 3D Space

Extending our previous Point structure, let’s create a new structure representing a point in three-dimensional space.

using System;

public struct Point3D
{
    public int X;
    public int Y;
    public int Z;

    public Point3D(int x, int y, int z)
    {
        X = x;
        Y = y;
        Z = z;
    }
}

public class Structures
{
    public static void Main(string[] args)
    {
        
        // Initializing a Point3D structure
        Point3D point3D = new Point3D(10, 20, 30);

        // Accessing structure members
        Console.WriteLine($"X: {point3D.X}, Y: {point3D.Y}, Z: {point3D.Z}");
        
    }
}

In this example, the Point3D structure adds a third dimension to the point representation. The initialization and member access follow the same principles as the previous example.

Date Range

Imagine a scenario where you need to represent a date range. Using a structure can provide a clean and efficient solution.

using System;

struct DateRange
{
    
    public DateTime StartDate;
    public DateTime EndDate;

    public DateRange(DateTime startDate, DateTime endDate)
    {
        StartDate = startDate;
        EndDate = endDate;
    }

    public TimeSpan Duration => EndDate - StartDate;
	
    public bool ContainsDate(DateTime date)
    {
        return date >= StartDate && date <= EndDate;
    }
	
}

public class Structures
{
    public static void Main(string[] args)
    {
        
        // Initializing a DateRange structure
        DateRange vacation = new DateRange(new DateTime(2023, 11, 25), new DateTime(2023, 11, 30));
		
        // Accessing structure members
        Console.WriteLine($"Start Date: {vacation.StartDate}, End Date: {vacation.EndDate}");
        Console.WriteLine($"Duration: {vacation.Duration.Days} days");
        
        // Contains Date
        Console.WriteLine($"Contains Date (2023-11-27): {vacation.ContainsDate(new DateTime(2023, 11, 27))}");
        
    }
}

In this example, the DateRange structure includes properties to calculate the duration of the range and check if a given date falls within the range.

Vector Operations

Consider a scenario where you need to perform vector operations in a physics simulation. You can create a Vector structure to represent a two-dimensional vector and define operations such as addition and multiplication:

using System;

public struct Vector
{
    
    public double X;
    public double Y;

    public Vector(double x, double y)
    {
        X = x;
        Y = y;
    }

    public static Vector Add(Vector a, Vector b)
    {
        return new Vector(a.X + b.X, a.Y + b.Y);
    }

    public static Vector Multiply(Vector vector, double scalar)
    {
        return new Vector(vector.X * scalar, vector.Y * scalar);
    }
}

public class Structures
{
    public static void Main(string[] args)
    {

        Vector vectorA = new Vector(12, 5);
        Vector vectorB = new Vector(7, 11);
        
        Vector vectorSum = Vector.Add(vectorA, vectorB);
        
        // Access vectorSum structure members
        Console.WriteLine($"X: {vectorSum.X}, Y: {vectorSum.Y}");
        
        Vector vectorProduct = Vector.Multiply(vectorA, 3);
        
        // Access vectorProduct structure members
        Console.WriteLine($"X: {vectorProduct.X}, Y: {vectorProduct.Y}");

    }
}

This example demonstrates how structures can encapsulate related data and operations in a concise and efficient manner.

Currency

Structures can also be employed for representing monetary values with specific currencies.

using System;

public struct Money
{
    
    public decimal Amount;
    public string CurrencyCode;

    public Money(decimal amount, string currencyCode)
    {
        Amount = amount;
        CurrencyCode = currencyCode;
    }

    public override string ToString()
    {
        return $"{Amount:C} ({CurrencyCode})";
    }
    
}

public class Structures
{
    public static void Main(string[] args)
    {
        
        // Initializing a Money structure
        Money price = new Money(99.99m, "USD");
        
        // Accessing structure members and displaying formatted output
        Console.WriteLine($"Product Price: {price}");

    }
}

In this example, the Money structure combines a decimal amount with a currency code, and the ToString method provides a formatted representation.

Conclusion

C# structures offer a powerful way to represent and manipulate data efficiently. Understanding when to use structures can lead to more robust and performant code. Whether you’re working with small, lightweight objects, requiring immutability, or dealing with value semantics, structures can be a valuable tool in your C# programming toolbox. Experiment with structures in your projects, and leverage their benefits to write cleaner, more efficient code.

Leave a Reply