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日 03:00

相关推荐

  • 1.2C#语言的标识符(Identifier)和关键字(Keyword)

    1.标识符 标识符必须以字母(A~Z 和 a~z)或 下划线(_) 开头,后续可以是任何字母(A~Z 和 a~z)、数字(0~9)和 下划线(_)。标识符区分大小写。 2.命名约定 标识符不应包含两个连续的__字符。这些名称是为编译器生成的标识符保留的。 3.关键字 关键字 是对编译器有特殊意义的预定义的保留…

    C#语言教程 2021年1月2日
    04800
  • 2.4C#函数成员之方法(Method)

    1.方法声明 2.方法分类 2.1成员方法 名称 修饰符 静态成员方法(static method) 用 static 修饰 实例成员方法(instance method) 无 static 修饰 2.2本地函数 本地函数是嵌套在类型里成员内部的private方法。 名称 修饰符 静态本地函数(static local…

    C#语言教程 2021年2月4日
    06060
  • 5.3C#异步编程完整示例

    以下将用”如何烹饪早餐”示例来解释异步: 1.同步阻塞 同步准备的早餐大约花费了 30 分钟,因为总耗时是每个任务耗时的总和。 一个厨师在烹饪第一份早餐的时候,不会同时准备第二份早餐。 此段代码不仅会阻塞方法的调用方,还会阻塞方法内的后续代码执行。 2.异步无阻塞 FryEggsAsync、Fr…

    C#语言教程 2021年5月3日
    06020

发表回复

登录后才能评论