【问题标题】:How to check record in c# 9 in NET 5 is immutable at runtime如何在 NET 5 中检查 c# 9 中的记录在运行时是不可变的
【发布时间】:2020-09-17 09:15:54
【问题描述】:

记录是 c#9、Net 5 中的一个新特性

我是said

如果您希望整个对象是不可变的并且表现得像一个值,那么您应该考虑将其声明为记录

在 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:仅初始化属性或索引器“Rectangle.Width”只能在对象初始化器中分配,或在实例构造函数或“init”访问器中的“this”或“base”上分配。

这是正确的,并确保记录是不可变的。

使用方法like 测试IsImmutable 类型给出false,因为在记录中没有生成只读属性。

如何查看c# 9中的记录,Net 5在运行时是不可变的,甚至有init property

【问题讨论】:

  • 你应该可以做到rectangle.Width.GetSetMethod() != null
  • 集合由roslyn编译器生成,用于记录。
  • 那么我假设它是私有的,因为你的分配不起作用,所以你可以检查它是否是公开的:rectangle.Width.GetSetMethod(true).isPublic
  • Setter 是公开的,类似于:` public int Width { get { return k__BackingField; } 设置 { k__BackingField = value; } } `

标签: immutability record .net-5 c#-9.0


【解决方案1】:

记录在运行时确实是可变的。这是故意的,是否意味着大多数序列化程序框架无需更新即可工作。

但是可以通过检查来检查属性是否为 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;

【讨论】:

    【解决方案2】:

    我认为不可能在运行时检查不变性。

    这里有一些生成的代码供您记录。您可以看到这两个属性都有一个公共设置器。

    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 是不可变的。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-01-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-05-09
      • 2021-01-27
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多