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:

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})";
}

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.

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.

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.

  • 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.

  • 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.

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>.

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