- 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
是基于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);
}