Structure types (C# reference)

A structure type (or struct type) is a value type that can encapsulate data and related functionality. You use the struct keyword to define a structure type:

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

    public double X { get; }
    public double Y { get; }

    public override string ToString() => $"({X}, {Y})";
}

Structure types have value semantics. That is, a variable of a structure type contains an instance of the type. By default, variable values are copied on assignment, passing an argument to a method, and returning a method result. In the case of a structure-type variable, an instance of the type is copied. For more information, see Value types.

Typically, you use structure types to design small data-centric types that provide little or no behavior. For example, .NET uses structure types to represent a number (both integer and real), a Boolean value, a Unicode character, a time instance. If you're focused on the behavior of a type, consider defining a class. Class types have reference semantics. That is, a variable of a class type contains a reference to an instance of the type, not the instance itself.

Because structure types have value semantics, we recommend you to define immutable structure types.

readonly struct

Beginning with C# 7.2, you use the readonly modifier to declare that a structure type is immutable. All data members of a readonly struct must be read-only as follows:

  • Any field declaration must have the readonly modifier
  • Any property, including auto-implemented ones, must be read-only. In C# 9.0 and later, a property may have an init accessor.

That guarantees that no member of a readonly struct modifies the state of the struct. In C# 8.0 and later, that means that other instance members except constructors are implicitly readonly.

Note

In a readonly struct, a data member of a mutable reference type still can mutate its own state. For example, you can't replace a List<T> instance, but you can add new elements to it.

The following code defines a readonly struct with init-only property setters, available in C# 9.0 and later:

public readonly struct Coords
{
    public Coords(double x, double y)
    {
        X = x;
        Y = y;
    }

    public double X { get; init; }
    public double Y { get; init; }

    public override string ToString() => $"({X}, {Y})";
}

readonly instance members

Beginning with C# 8.0, you can also use the readonly modifier to declare that an instance member doesn't modify the state of a struct. If you can't declare the whole structure type as readonly, use the readonly modifier to mark the instance members that don't modify the state of the struct.

Within a readonly instance member, you can't assign to structure's instance fields. However, a readonly member can call a non-readonly member. In that case the compiler creates a copy of the structure instance and calls the non-readonly member on that copy. As a result, the original structure instance is not modified.

Typically, you apply the readonly modifier to the following kinds of instance members:

  • methods:

    public readonly double Sum()
    {
        return X + Y;
    }
    

    You can also apply the readonly modifier to methods that override methods declared in System.Object:

    public readonly override string ToString() => $"({X}, {Y})";
    
  • properties and indexers:

    private int counter;
    public int Counter
    {
        readonly get => counter;
        set => counter = value;
    }
    

    If you need to apply the readonly modifier to both accessors of a property or indexer, apply it in the declaration of the property or indexer.

    Note

    The compiler declares a get accessor of an auto-implemented property as readonly, regardless of presence of the readonly modifier in a property declaration.

    In C# 9.0 and later, you may apply the readonly modifier to a property or indexer with an init accessor:

    public readonly double X { get; init; }
    

You can't apply the readonly modifier to static members of a structure type.

The compiler may make use of the readonly modifier for performance optimizations. For more information, see Write safe and efficient C# code.

Nondestructive mutation

Beginning with C# 10, you can use the with expression to produce a copy of a structure-type instance with the specified properties and fields modified. You use object initializer syntax to specify what members to modify and their new values, as the following example shows:

public readonly struct Coords
{
    public Coords(double x, double y)
    {
        X = x;
        Y = y;
    }

    public double X { get; init; }
    public double Y { get; init; }

    public override string ToString() => $"({X}, {Y})";
}

public static void Main()
{
    var p1 = new Coords(0, 0);
    Console.WriteLine(p1);  // output: (0, 0)

    var p2 = p1 with { X = 3 };
    Console.WriteLine(p2);  // output: (3, 0)

    var p3 = p1 with { X = 1, Y = 4 };
    Console.WriteLine(p3);  // output: (1, 4)
}

Limitations with the design of a structure type

When you design a structure type, you have the same capabilities as with a class type, with the following exceptions:

  • You can't declare a parameterless constructor. Every structure type already provides an implicit parameterless constructor that produces the default value of the type.

    Note

    Beginning with C# 10, you can declare a parameterless constructor in a structure type. For more information, see the Parameterless constructors and field initializers section.

  • You can't initialize an instance field or property at its declaration. However, you can initialize a static or const field or a static property at its declaration.

    Note

    Beginning with C# 10, you can initialize an instance field or property at its declaration. For more information, see the Parameterless constructors and field initializers section.

  • A constructor of a structure type must initialize all instance fields of the type.

  • A structure type can't inherit from other class or structure type and it can't be the base of a class. However, a structure type can implement interfaces.

  • You can't declare a finalizer within a structure type.

Parameterless constructors and field initializers

Beginning with C# 10, you can declare a parameterless instance constructor in a structure type, as the following example shows:

public readonly struct Measurement
{
    public Measurement()
    {
        Value = double.NaN;
        Description = "Undefined";
    }

    public Measurement(double value, string description)
    {
        Value = value;
        Description = description;
    }

    public double Value { get; init; }
    public string Description { get; init; }

    public override string ToString() => $"{Value} ({Description})";
}

public static void Main()
{
    var m1 = new Measurement();
    Console.WriteLine(m1);  // output: NaN (Undefined)

    var m2 = default(Measurement);
    Console.WriteLine(m2);  // output: 0 ()

    var ms = new Measurement[2];
    Console.WriteLine(string.Join(", ", ms));  // output: 0 (), 0 ()
}

As the preceding example shows, the default value expression ignores a parameterless constructor and produces the default value of a structure type, which is the value produced by setting all value-type fields to their default values (the 0-bit pattern) and all reference-type fields to null. Structure-type array instantiation also ignores a parameterless constructor and produces an array populated with the default values of a structure type.

Beginning with C# 10, you can also initialize an instance field or property at its declaration, as the following example shows:

public readonly struct Measurement
{
    public Measurement(double value)
    {
        Value = value;
    }

    public Measurement(double value, string description)
    {
        Value = value;
        Description = description;
    }

    public double Value { get; init; }
    public string Description { get; init; } = "Ordinary measurement";

    public override string ToString() => $"{Value} ({Description})";
}

public static void Main()
{
    var m1 = new Measurement(5);
    Console.WriteLine(m1);  // output: 5 (Ordinary measurement)

    var m2 = new Measurement();
    Console.WriteLine(m2);  // output: 0 ()
}

If you don't declare a parameterless constructor explicitly, a structure type provides a parameterless constructor whose behavior is as follows:

  • If a structure type has explicit instance constructors or has no field initializers, an implicit parameterless constructor produces the default value of a structure type, regardless of field initializers, as the preceding example shows.

  • If a structure type has no explicit instance constructors and has field initializers, the compiler synthesizes a public parameterless constructor that performs the specified field initializations, as the following example shows:

    public struct Coords
    {
        public double X = double.NaN;
        public double Y = double.NaN;
    
        public override string ToString() => $"({X}, {Y})";
    }
    
    public static void Main()
    {
        var p1 = new Coords();
        Console.WriteLine(p1);  // output: (NaN, NaN)
    
        var p2 = default(Coords);
        Console.WriteLine(p2);  // output: (0, 0)
    
        var ps = new Coords[3];
        Console.WriteLine(string.Join(", ", ps));  // output: (0, 0), (0, 0), (0, 0)
    }
    

As the preceding example shows, the default value expression and array instantiation ignore field initializers.

For more information, see the Parameterless struct constructors feature proposal note.

Instantiation of a structure type

In C#, you must initialize a declared variable before it can be used. Because a structure-type variable can't be null (unless it's a variable of a nullable value type), you must instantiate an instance of the corresponding type. There are several ways to do that.

Typically, you instantiate a structure type by calling an appropriate constructor with the new operator. Every structure type has at least one constructor. That's an implicit parameterless constructor, which produces the default value of the type. You can also use a default value expression to produce the default value of a type.

If all instance fields of a structure type are accessible, you can also instantiate it without the new operator. In that case you must initialize all instance fields before the first use of the instance. The following example shows how to do that:

public static class StructWithoutNew
{
    public struct Coords
    {
        public double x;
        public double y;
    }

    public static void Main()
    {
        Coords p;
        p.x = 3;
        p.y = 4;
        Console.WriteLine($"({p.x}, {p.y})");  // output: (3, 4)
    }
}

In the case of the built-in value types, use the corresponding literals to specify a value of the type.

Passing structure-type variables by reference

When you pass a structure-type variable to a method as an argument or return a structure-type value from a method, the whole instance of a structure type is copied. That can affect the performance of your code in high-performance scenarios that involve large structure types. You can avoid value copying by passing a structure-type variable by reference. Use the ref, out, or in method parameter modifiers to indicate that an argument must be passed by reference. Use ref returns to return a method result by reference. For more information, see Write safe and efficient C# code.

ref struct

Beginning with C# 7.2, you can use the ref modifier in the declaration of a structure type. Instances of a ref struct type are allocated on the stack and can't escape to the managed heap. To ensure that, the compiler limits the usage of ref struct types as follows:

  • A ref struct can't be the element type of an array.
  • A ref struct can't be a declared type of a field of a class or a non-ref struct.
  • A ref struct can't implement interfaces.
  • A ref struct can't be boxed to System.ValueType or System.Object.
  • A ref struct can't be a type argument.
  • A ref struct variable can't be captured by a lambda expression or a local function.
  • A ref struct variable can't be used in an async method. However, you can use ref struct variables in synchronous methods, for example, in those that return Task or Task<TResult>.
  • A ref struct variable can't be used in iterators.

Typically, you define a ref struct type when you need a type that also includes data members of ref struct types:

public ref struct CustomRef
{
    public bool IsValid;
    public Span<int> Inputs;
    public Span<int> Outputs;
}

To declare a ref struct as readonly, combine the readonly and ref modifiers in the type declaration (the readonly modifier must come before the ref modifier):

public readonly ref struct ConversionRequest
{
    public ConversionRequest(double rate, ReadOnlySpan<double> values)
    {
        Rate = rate;
        Values = values;
    }

    public double Rate { get; }
    public ReadOnlySpan<double> Values { get; }
}

In .NET, examples of a ref struct are System.Span<T> and System.ReadOnlySpan<T>.

struct constraint

You also use the struct keyword in the struct constraint to specify that a type parameter is a non-nullable value type. Both structure and enumeration types satisfy the struct constraint.

Conversions

For any structure type (except ref struct types), there exist boxing and unboxing conversions to and from the System.ValueType and System.Object types. There exist also boxing and unboxing conversions between a structure type and any interface that it implements.

C# language specification

For more information, see the Structs section of the C# language specification.

For more information about features introduced in C# 7.2 and later, see the following feature proposal notes:

See also