问题描述
记录是c#9 Net 5中的一项新功能
是said
如果您希望整个对象都是不可变的,并且表现得像一个值,那么您应该考虑将其声明为记录
在NET 5的c#9中创建记录:
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”访问器的“ this”或“ base”上分配仅初始化属性或索引器“ Rectangle.Width”。
那是正确的,并确保记录是不变的。
使用方法like测试IsImmutable类型是否为false,因为在记录中没有生成的只读属性。
如何检查c#9中的记录,Net 5在运行时是不可变的,甚至具有init property
?
解决方法
记录在运行时确实是可变的。这是有意的,因为这意味着大多数序列化程序框架无需更新即可工作。
不过,可以通过检查以下内容来检查属性是否仅为initit:
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;
,
我认为不可能在运行时检查不变性。
以下是一些生成的代码供您记录。您可以看到这两个属性都有一个公共设置器。
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访问器。
所以我看不到任何方法可以在运行时检查Rectangle是不可变的。