C# --- Struct and Record

本文介绍了C#中Struct和Record两种数据类型。Struct是值类型,与class有诸多不同,如不支持继承、支持值比对等。Record基于class和struct,分为class based record和struct based record,编译器会为其添加特定方法,还能便捷实现非破坏性修改,编译时会重写比较方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

C# --- Struct and Record

Struct

  • struct是一种数据类型, 和class非常类似, 主要有以下的不同
  • struct是value type, class是reference type
  • 因为是value type所以strcut不是必须储存在heap上
  • struct不能等于null, The default value for a struct is an empty instance, with all fields empty (set to their default values).
  • struct 支持值比对, 也就是可以直接用等于号比较
  • struct 不支持继承
struct Point
{
	 int x, y;
	 public Point (int x, int y) { this.x = x; this.y = y; } 
}

struct 的 default constructor

  • a struct always has an implicit parameterless constructor.
Point p = new Point(); // p.x and p.y will be 0
struct Point { int x, y; }
  • Even when you define a parameterless constructor of your own, the implicit parameterless constructor still exists and can be accessed via the default keyword:
Point p1 = new Point(); // p1.x and p1.y will be 1
Point p2 = default; // p2.x and p2.y will be 0
struct Point
{
	int x = 1;
	int y;
	public Point() => y = 1;
}

Record

  • record 是基于class和struct的数据类型, 也就是分为 class based record 和 struct based record, struct可以包含fields, properties, method等
  • record 可以实现interface, 也可以拥有继承关系 (class-based record)
  • record默认是class based record: record Point { } // Point is a class
  • struct based record: record struct Point { } // Point is a struct

一个简单的record

record Point
{
	 public Point (double x, double y) => (X, Y) = (x, y); //(X, Y) = (x, y); 等于 { this.X = x; this.Y = y; }
	 public double X { get; init; }
	 public double Y { get; init; } 
}
  • 在编译阶段, 以上的代码会被加入, 如下代码所示
  • 编译器加入 protected copy constructor 和 hidden clone method 帮助实现 nondestructive mutation
  • 重写比较方法(equal, hashcode等方法) 帮助实现值对比
  • 重写toString() 方法
class Point
{ 
	 public Point (double x, double y) => (X, Y) = (x, y);
	 public double X { get; init; }
	 public double Y { get; init; } 
	 protected Point (Point original) // “Copy constructor”
	 {
	 	this.X = original.X; this.Y = original.Y
 	 }
 	 // This method has a strange compiler-generated name:
	 public virtual Point <Clone>$() => new Point (this); // Clone method
 	// Additional code to override Equals, ==, !=, GetHashCode, ToString()
 	// ...
}

record的parameter list

  • record的后面可以用括号包括parameter list, 在初始化时可以直接 var = new Point(12, 4)
record Point (double X, double Y)
{
 // You can optionally define additional class members here...
}
  • 编译器会对parameter list进行如下操作
  • 给每个parameter构建 init-only property
  • 构建primary constructor
  • 构建deconstructor

Nondestructive Mutation

  • 当我们需要对一个immutable object进行修改的时候, 我们需要创建一个新的object然后对原来的object进行copy以及修改, 这就是Nondestructive Mutation. 但是当property很多的时候, 就会很麻烦
  • record提供了更便捷的方法实现 Nondestructive Mutation, 使用with keyword
Point p1 = new Point (3, 3);
Point p2 = p1 with { Y = 4 };
Console.WriteLine (p2); // Point { X = 3, Y = 4 }
record Point (double X, double Y);
  • In this example, p2 is a copy of p1, but with its Y property set to 4. The benefit is
    more apparent when there are more properties:
Test t1 = new Test (1, 2, 3, 4, 5, 6, 7, 8);
Test t2 = t1 with { A = 10, C = 30 };
Console.WriteLine (t2);
record Test (int A, int B, int C, int D, int E, int F, int G, int H);
//Here’s the output:
Test { A = 10, B = 2, C = 30, D = 4, E = 5, F = 6, G = 7, H = 8 }

Record and Equality Comparsion

  • record在编译时, 编译器会重写equal, hashcode等比较方法, 所以可以直接比较, 不需要像reference type一样重写equal和hashcode.
var p1 = new Point (1, 2);
var p2 = new Point (1, 2);
Console.WriteLine (p1.Equals (p2)); // True
record Point (double X, double Y);
  • The equality operator also works with records (as it does with tuples):
Console.WriteLine (p1 == p2);  // True
  • 如果record包含 lazy values, transient’ value, arrays, 或者 collection type 或者 自定义的等于比较时, 需要重写equals 当然也需要重写GetHashCode()
record Point (double X, double Y)
{
	double _someOtherField;
	public virtual bool Equals (Point other) =>
	other != null && X == other.X && Y == other.Y;
}

Use case

public record settingscope( [property: BsonIgnoreIfNull] string IdSettingscopeType Type)
{
	public static Settingscope Global { get; } = new(null, SettingScopeType.Global);
}

### C# Struct 类型使用指南 #### 特殊类型的 `ref struct` `ref struct` 不同于常规的 `struct`,虽然两者均为值类型,但是 `ref struct` 只能在栈上分配内存,并且不允许装箱至堆中。当传递一个 `ref struct` 给某个方法时,实际上是在传递其引用而非复制整个对象实例[^1]。 ```csharp public ref struct MyRefStruct { public int Value; } ``` #### 结构体中的字段初始化器与无参构造函数 自 C# 10 起,在定义结构体时可以为其成员变量设置初始值以及实现默认构造逻辑。不过需要注意的是,默认构造行为仅会在显式调用构造器的时候触发;如果采用 `default` 关键字创建,则不会执行任何定制化的初始化过程。此特性特别适用于 `record struct` 这样的新型数据载体设计模式[^2]。 ```csharp public struct Person { private string name; // 字段初始化表达式 public DateTime BirthDate { get; } = DateTime.Now.AddYears(-30); // 默认构造函数 public Person() : this("Unknown") {} // 带参数的构造函数 public Person(string name) => Name = name ?? throw new ArgumentNullException(nameof(name)); public string Name { get => name; set => name = value ?? "Default"; } } // 创建新实例并应用构造器 var personInstance = new Person(); Console.WriteLine(personInstance.BirthDate); ``` #### 托管环境下的类型体系 .NET Framework 提供了一套丰富的类库来支持开发者构建应用程序。这套框架由众多按照特定领域划分而成的命名空间组成,并被打包进不同的程序集中以便加载运行。CLR(公共语言运行时)负责管理这些资源并与操作系统交互,共同构成了完整的 .NET 平台生态链[^3]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值