开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第24天,点击查看活动详情
structure type
或 struct type
结构类型,也常常称为结构体或结构体类型。它是可以封装数据和相关功能(函数)的值类型。
值类型
结构类型
结构类型的定义和介绍
struct
关键字用于定义结构类型。
比如,定义一个表示坐标的结构:
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})";
}
结构类型的变量包含该类型的实例(值类型的值语义性(value semantics
)),默认,在赋值、传递参数、返回方法结果时,变量的值被复制。
由于值语义性,通常应该将结构类型定义为 不可变结构类型(
immutable structure types
)
推荐使用结构类型设计以数据为主(数据为中心)的较小类型,通常没有行为或者行为很少(尽量不定义方法)。
.NET 中的数字(整数和实数)、布尔值、Unicode字符、time实例等都是结构类型。
当更关注于类型的行为,则推荐使用class,class类型有引用语义性(reference semantics
),class类型的变量包含对实例的引用,而不是实例本身。
readonly struct 不可变结构类型
readonly
修饰符(modifier)可以声明一个结构类型不可变。
只读结构体的数据成员是下列的只读形式:
- 任何字段都要使用
readonly
修饰符 - 任何属性,包括自动生成属性,必须是只读的。C# 9.0及之后的版本,属性可以有初始化访问器。
这样,只读结构体可以做到其成员不被修改,结构的状态不变。除了构造器(构造函数),所有实例成员都是只读的。
比如下面将 Coords
修改为只读,属性设置只能在初始化时。
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 实例成员
可以为不会修改结构状态的实例成员添加 readonly 修饰符声明。在无法将整个结构体类型声明为readonly
时,推荐将合适的实例成员声明为readonly
。
readonly
不能用于结构类型的实例字段,readonly
成员可以调用非readonly
成员(编译器会创建结构体实例的副本,并调用副本的非readonly
成员,原始的结构体实例不会被修改)。
- 修饰方法
public readonly double Sum()
{
return X + Y;
}
还可以将readonly
修饰符应用于System.Object
中声明的重写(override
)方法。
public readonly override string ToString() => $"({X}, {Y})";
- 修饰属性和索引器
private int counter;
public int Counter
{
readonly get => counter;
set => counter = value;
}
// 有set访问器的属性不能修饰为readonly
// public readonly int MyCounter { get; set; }
public readonly double X { get; init; }
比如下面,一个结构类型的示例:
internal struct StructureType
{
internal readonly int Id { get; init; }
internal string Name { get; set; }
}
// 或
internal struct StructureType
{
internal readonly int Id { get; init; }
internal string Name { get; set; }
public StructureType()
{
Id = 10;
Name = "abc";
}
}
// StructureType structureType = new StructureType();
StructureType structureType = new StructureType() {
Id= 10
};
//structureType.Id = 10; // 无法为readonly 赋值
Console.WriteLine(structureType.Id);
附:关于 C# 9 引入的初始化访问器(init accessor)
从C# 9开始,可以使用 init
关键字为属性或索引器定义一个访问器方法,它 可以使属性或索引器只能在对象构造时被赋值,这样可以强制实现不变性,在初始化后不能被改变。
如下,是在自动生成属性(auto-implemented propertie
)中使用初始化访问器的示例:
class Person_InitExampleAutoProperty
{
public int YearOfBirth { get; init; }
}
为初始化访问器的属性赋值,只能在创建对象时,其他地方赋值,编译器或vs会报错CS8852
:
var person = new Person_InitExampleAutoProperty()
{
YearOfBirth = 1970
};
// person.YearOfBirth = 1970; // 错误 CS8852 只能在对象初始值设定项中或在实例构造函数或 "init" 访问器中的 "this" 或 "base" 上分配 init-only 属性或索引器
初始化访问器在属性值保存在私有字段的声明中的使用,也就是替代了set
的作用:
class Person_InitExample
{
private int _yearOfBirth;
public int YearOfBirth
{
get { return _yearOfBirth; }
init { _yearOfBirth = value; }
}
}
初始化访问器还可以作为表达式主体成员(expression-bodied member
)使用
class Person_InitExampleExpressionBodied
{
private int _yearOfBirth;
public int YearOfBirth
{
get => _yearOfBirth;
init => _yearOfBirth = value;
}
}