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 asreadonly
, regardless of presence of thereadonly
modifier in a property declaration.In C# 9.0 and later, you may apply the
readonly
modifier to a property or indexer with aninit
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 anasync
method. However, you can useref
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: