2

Record is a new feature in c#9, Net 5

It's said

If you want the whole object to be immutable and behave like a value, then you should consider declaring it as a record

Creating a record in c#9 , NET 5:

public record Rectangle
{
    public int Width { get; init; }
    public int Height { get; init; }
}

Then instantiating it:

var rectangle = new Rectangle (20,30);

Trying to change the value:

rectange.Width=50; //compiler error

Compiler raise the error:

error CS8852: Init-only property or indexer 'Rectangle.Width' can only be assigned in an object initializer, or on 'this' or 'base' in an instance constructor or an 'init' accessor.

That is right and insure that the record is immutable.

Using a method like to test IsImmutable type give false, because in record there is no generated readonly properties.

How to check the record in c# 9, Net 5 is immutable at runtime or even it has init property?

M.Hassan
  • 10,282
  • 5
  • 65
  • 84

2 Answers2

4

A record is indeed mutable at runtime. This is intentional, is it means most serializer frameworks work without updating.

It is however possible to check if a property is initonly by checking:

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;
Olivier Jacot-Descombes
  • 104,806
  • 13
  • 138
  • 188
Yair Halberstadt
  • 5,733
  • 28
  • 60
-2

I don't think that it's possible to check for immutability at runtime.

Here's some of the generated code for your record. You can see that both properties have a public 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;
        }
    }

The following code will compile and run without errors.

            var rect = new Rectangle { Height = 1, Width = 2 };
            typeof(Rectangle).GetProperty("Height").SetValue(rect, 5);
            Console.Write(rect.Height);
            //Prints 5

At runtime the init accessor is just a regular setter. It's only at compile time that a check is made to only allow init accessor to be called during object initialization.

So I don't see any way to check at runtime that Rectangle is immutable.

J.Loscos
  • 2,478
  • 1
  • 10
  • 17