3.2C#值类型之结构(Struct)

1.结构声明

struct 类型定义包含数据成员和函数成员的结构,这一点与类类型相似。

struct 结构名<类型参数> : 接口
{
    //结构体
}

2.继承

所有结构类型只能隐式派生自 System.ValueType类,结构类型不能从其它类或结构类型继承,也不能作为类的基础类型,因为结构类型是隐式密封的。

3.构造函数

如果显式声明一个无参数构造函数,那创建对象调用无参数构造函数时将调用该显式声明的无参数构造函数。

如果未显式声明一个无参数构造函数,那结构类型会自动提供一个隐式无参数构造函数,行为如下:

  • 如果结构类型有显式的实例有参数构造函数或无字段初始值设定项,隐式的无参数构造函数会生成结构类型的默认值。
  • 如果结构类型没有显式的实例有参数构造函数和有字段初始值设定项,编译器会合成一个执行字段初始值设定项的public无参数构造函数。

default(T)默认值表达式会忽略无论显式还是隐式的无参数构造函数和字段初始值设定项并生成结构类型的默认值。

备注:默认值是通过如下设置生成的值:将所有值类型的字段设置为其默认值(0 位模式),将所有引用类型的字段设置为 null

4.创建对象

结构的创建对象与类的创建对象相似。

5.结构与类的区别

类是引用类型。创建类的对象后,向其分配对象的变量仅保留对相应内存的引用。将对象引用分配给新变量后,新变量会引用原始对象。通过一个变量所做的更改将反映到另一个变量中,因为它们引用相同的数据。

结构是值类型。创建结构时,向其分配结构的变量保留结构的实际数据。 将结构分配给新变量时,会复制结构。 因此,新变量和原始变量包含相同数据的副本(共两个)。对一个副本所做的更改不会影响另一个副本。

一般来说,类用于对更复杂的行为建模。类通常存储计划在创建类对象后进行修改的数据。结构最适用于小型数据结构。结构通常存储不打算在创建结构后修改的数据。

6.分部(partial)结构

拆分一个结构的定义到两个或更多的文件中,每个文件包含类型或方法定义的一部分,编译应用程序时将把所有部分组合起来。

partial struct S1
{
    void Struct_Test() { }
}

partial struct S1
{
    void Struct_Test2() { }
}
//合并后
struct S1
{
    void Struct_Test() { }
    void Struct_Test2() { }
}

7.非破坏性变化(nondestructive mutation)

非破坏性变化(nondestructive mutation)引用的是函数式编程里面的概念,意思是当您想要更改一个对象的状态时,创建原数据的拷贝与修改从而合成新数据优于直接修改原数据。

with表达式会创建新的对象,此新的对象是已存在对象的副本与修改的特定属性和字段合成的新对象。使用对象初始值设定项语法来指定要修改的成员及其新值。

对于引用类型成员,在复制操作数时仅复制对成员实例的引用。

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

8.readonly修饰符

如果将整个结构类型声明为 readonly ,则该结构类型是不可变类型。

所有字段声明都必须具有readonly修饰符。

所有属性(包括自动实现的属性)都必须为只读属性,无需添加readonly修饰符。

不过,可变的引用类型的数据成员仍可改变其自身的状态。例如,不能替换 List<T> 实例,但可以向其中添加新元素。

readonly struct 结构名
{
    //结构体
}

如果不能将整个结构类型声明为 readonly,因为结构类型是可变类型,可使用 readonly 修饰符标记不会修改结构状态的实例成员(方法、属性、索引器),不能将 readonly 修饰符应用于结构类型的静态成员。

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

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

9.ref修饰符

ref 结构类型的实例在堆栈上分配,并且不能转义到托管堆。

在 .NET 中,ref 结构的示例分别是 System.Span<T> 和 System.ReadOnlySpan<T>

//将整个结构类型声明为ref
ref struct 结构名
{
    //结构体
}

ref结构有如下限制:

  • ref 结构不能是数组的元素类型。
  • ref 结构不能是类或非 ref 结构的字段的声明类型。
  • ref 结构不能实现接口。
  • ref 结构不能被装箱为 System.ValueType 或 System.Object
  • ref 结构不能是类型参数。
  • ref 结构变量不能由 lambda 表达式或本地函数捕获。
  • ref 结构变量不能在 async 方法中使用。 但是,可以在同步方法中使用 ref 结构变量,例如,在返回 Task 或 Task<TResult> 的方法中。
  • ref 结构变量不能在迭代器中使用。

10.readonly ref修饰符

若要将 ref 结构声明为 readonly,请在类型声明中组合使用 readonly 修饰符和 ref 修饰符(readonly 修饰符必须位于 ref 修饰符之前)。

readonly span所使用的内存仅限于单个堆栈帧,并且readonly span使用的内存无法进行修改。

原创文章,作者:huoxiaoqiang,如若转载,请注明出处:https://www.huoxiaoqiang.com/csharp/csharplang/7873.html

(0)
上一篇 2021年3月2日 17:53
下一篇 2021年3月3日 22:22

相关推荐

  • 3.14C#类型之指针(Pointer)

    1.指针类型声明 类型为referent类型*的指针变量存储的是类型为referent类型的固定变量或可移动变量的地址。只有非托管类型可为referent类型。 任何指针类型可以分配 null。 2.继承 指针类型没有从 System.Object 继承。 3.类型转换 指针类型与 Sys…

    C#语言教程 2021年3月14日
    06990
  • 2.9C#函数成员之构造函数(Constructor)

    1.构造函数声明 构造函数的声明方式与方法相似,与所属结构或类同名,不过没有返回类型。 如果没有为结构或类提供实例构造函数,则会隐式自动提供无参数的实例构造函数,这时此结构或类可以被实例化。 不过,如果显式地使用 private 修饰符修饰无参数的空实例构造函数会清楚地表明该类不能被实例化。 2.构…

    C#语言教程 2021年2月9日
    05010
  • 2.1C#语言的变量(Variable)

    1.变量声明和初始化 2.变量分类 x 是一个静态变量, y 它是一个实例变量,a 是一个值参数,b 是一个引用参数,c 是一个输出参数,v[0] 是一个数组元素,i 是一个局部变量。 2.1成员变量 名称 修饰符 静态变量 用 static 修饰 实例变量 —————— 2.2局部变量 …

    C#语言教程 2021年2月1日
    05360

发表回复

登录后才能评论