如何在NET5中的c#9中检查记录在运行时是不可变的



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访问器。

所以我看不出有任何方法可以在运行时检查矩形是不可变的。

最新更新