Record是c#9,Net5 中的一个新功能
据说
如果您希望整个对象是不可变的,并且表现得像一个值,那么您应该考虑将其声明为记录
在c#9,NET 5:中创建记录
public record Rectangle
{
public int Width { get; init; }
public int Height { get; init; }
}
然后实例化它:
var rectangle = new Rectangle (20,30);
尝试更改值:
rectange.Width=50; //compiler error
编译器引发错误:
错误CS8852:Init-only属性或索引器"Rectangle.Width"只能在对象初始值设定项中赋值,或者在实例构造函数或"Init"访问器中的"this"或"base"上赋值。
这是正确的,并确保记录是不可变的。
使用类似于测试IsImmutable类型的方法会给出false,因为在记录中并没有生成只读属性。
如何检查c#9中的记录,Net5在运行时是不可变的,甚至它有init property
?
记录在运行时确实是可变的。这是有意的,因为这意味着大多数序列化程序框架在不更新的情况下工作。
然而,可以通过检查来检查属性是否是initonly
public static bool IsInitOnly(PropertyInfo propertyInfo)
{
return propertyInfo?.SetMethod.ReturnParameter
.GetRequiredCustomModifiers()
.Any(x => x.FullName == _isExternalInitName)
?? false;
}
private static string _isExternalInitName =
typeof(System.Runtime.CompilerServices.IsExternalInit).FullName;
我认为不可能在运行时检查不变性。
以下是为您的记录生成的一些代码。您可以看到这两个属性都有一个公共setter。
public class Rectangle : IEquatable<Rectangle>
{
[CompilerGenerated]
private readonly int <Width>k__BackingField;
[CompilerGenerated]
private readonly int <Height>k__BackingField;
protected virtual Type EqualityContract
{
[CompilerGenerated]
get
{
return typeof(Rectangle);
}
}
public int Width
{
[CompilerGenerated]
get
{
return <Width>k__BackingField;
}
[CompilerGenerated]
set
{
<Width>k__BackingField = value;
}
}
public int Height
{
[CompilerGenerated]
get
{
return <Height>k__BackingField;
}
[CompilerGenerated]
set
{
<Height>k__BackingField = value;
}
}
以下代码将在没有错误的情况下编译和运行。
var rect = new Rectangle { Height = 1, Width = 2 };
typeof(Rectangle).GetProperty("Height").SetValue(rect, 5);
Console.Write(rect.Height);
//Prints 5
在运行时,init访问器只是一个常规的setter。只有在编译时,才会检查是否只允许在对象初始化期间调用init访问器。
所以我看不出有任何方法可以在运行时检查矩形是不可变的。