结构类型(C# 参考)Structure types (C# reference)

结构类型(“structure type”或“struct type”)是一种可封装数据和相关功能的值类型A structure type (or struct type) is a value type that can encapsulate data and related functionality. 使用 struct 关键字定义结构类型: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. 例如,.NET 使用结构类型来表示数字(整数实数)、布尔值Unicode 字符以及时间实例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 结构readonly struct

从 C# 7.2 开始,可以使用 readonly 修饰符来声明结构类型不可变: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; }
    public double Y { get; }

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

readonly 结构的所有数据成员都必须是只读的,如下所示:All data members of a readonly struct must be read-only as follows:

  • 任何字段声明都必须具有 readonly 修饰符Any field declaration must have the readonly modifier
  • 任何属性(包括自动实现的属性)都必须是只读的Any property, including auto-implemented ones, must be read-only

这样可以保证 readonly 结构的成员不会修改该结构的状态。That guarantees that no member of a readonly struct modifies the state of the struct.

备注

readonly 结构中,可变引用类型的数据成员仍可改变其自身的状态。In a readonly struct, a data member of a mutable reference type still can mutate its own state. 例如,不能替换 List<T> 实例,但可以向其中添加新元素。For example, you can't replace a List<T> instance, but you can add new elements to it.

readonly 实例成员readonly instance members

从 C#8.0 开始,还可以使用 readonly 修饰符声明实例成员不会修改结构的状态。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. 如果不能将整个结构类型声明为 readonly,可使用 readonly 修饰符标记不会修改结构状态的实例成员。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. readonly 结构中,每个实例成员都是隐式的 readonlyIn a readonly struct, every instance member is implicitly readonly.

readonly 实例成员内,不能分配到结构的实例字段。Within a readonly instance member, you can't assign to structure's instance fields. 但是,readonly 成员可以调用非 readonly 成员。However, a readonly member can call a non-readonly member. 在这种情况下,编译器将创建结构实例的副本,并调用该副本上的非 readonly 成员。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.

通常,将 readonly 修饰符应用于以下类型的实例成员:Typically, you apply the readonly modifier to the following kinds of instance members:

  • 方法:methods:

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

    还可以将 readonly 修饰符应用于可替代在 System.Object 中声明的方法的方法: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;
    }
    

    如果需要将 readonly 修饰符应用于属性或索引器的两个访问器,请在属性或索引器的声明中应用它。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.

    备注

    编译器会将自动实现的属性get 访问器声明为 readonly,而不管属性声明中是否存在 readonly 修饰符。The compiler declares a get accessor of an auto-implemented property as readonly, regardless of presence of the readonly modifier in a property declaration.

不能将 readonly 修饰符应用于结构类型的静态成员。You can't apply the readonly modifier to static members of a structure type.

编译器可以使用 readonly 修饰符进行性能优化。The compiler may make use of the readonly modifier for performance optimizations. 有关详细信息,请参阅编写安全有效的 C# 代码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

在 C# 中,必须先初始化已声明的变量,然后才能使用该变量。In C#, you must initialize a declared variable before it can be used. 由于结构类型变量不能为 null(除非它是可为空的值类型的变量),因此,必须实例化相应类型的实例。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.

通常,可使用 new 运算符调用适当的构造函数来实例化结构类型。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.

如果结构类型的所有实例字段都是可访问的,则还可以在不使用 new 运算符的情况下对其进行实例化。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. 使用 refoutin 方法参数修饰符,指示必须按引用传递参数。Use the ref, out, or in method parameter modifiers to indicate that an argument must be passed by reference. 使用 ref 返回值按引用返回方法结果。Use ref returns to return a method result by reference. 有关详细信息,请参阅编写安全有效的 C# 代码For more information, see Write safe and efficient C# code.

ref 结构ref struct

从 C# 7.2 开始,可以在结构类型的声明中使用 ref 修饰符。Beginning with C# 7.2, you can use the ref modifier in the declaration of a structure type. ref 结构类型的实例在堆栈上分配,并且不能转义到托管堆。Instances of a ref struct type are allocated on the stack and can't escape to the managed heap. 为了确保这一点,编译器将 ref 结构类型的使用限制如下:To ensure that, the compiler limits the usage of ref struct types as follows:

  • ref 结构不能是数组的元素类型。A ref struct can't be the element type of an array.
  • ref 结构不能是类或非 ref 结构的字段的声明类型。A ref struct can't be a declared type of a field of a class or a non-ref struct.
  • ref 结构不能实现接口。A ref struct can't implement interfaces.
  • ref 结构不能被装箱为 System.ValueTypeSystem.ObjectA ref struct can't be boxed to System.ValueType or System.Object.
  • ref 结构不能是类型参数。A ref struct can't be a type argument.
  • ref 结构变量不能由 lambda 表达式本地函数捕获。A ref struct variable can't be captured by a lambda expression or a local function.
  • ref 结构变量不能在 async 方法中使用。A ref struct variable can't be used in an async method. 但是,可以在同步方法中使用 ref 结构变量,例如,在返回 TaskTask<TResult> 的方法中。However, you can use ref struct variables in synchronous methods, for example, in those that return Task or Task<TResult>.
  • ref 结构变量不能在迭代器中使用。A ref struct variable can't be used in iterators.

通常,如果需要一种同时包含 ref 结构类型的数据成员的类型,可以定义 ref 结构类型: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;
}

若要将 ref 结构声明为 readonly,请在类型声明中组合使用 readonly 修饰符和 ref 修饰符(readonly 修饰符必须位于 ref 修饰符之前):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; }
}

在 .NET 中,ref 结构的示例分别是 System.Span<T>System.ReadOnlySpan<T>In .NET, examples of a ref struct are System.Span<T> and System.ReadOnlySpan<T>.

转换Conversions

对于任何结构类型(ref struct 类型除外),都存在与 System.ValueTypeSystem.Object 类型之间的装箱和取消装箱相互转换。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# 语言规范C# language specification

有关详细信息,请参阅 C# 语言规范中的结构部分。For more information, see the Structs section of the C# language specification.

有关 C#7.2 及更高版本中引入的功能的更多信息,请参见以下功能建议说明:For more information about features introduced in C# 7.2 and later, see the following feature proposal notes:

请参阅See also