Open sandboxFocusImprove this doc

SR0004: Read-Only Field in a PSerializable Type

Severity Error
Reported by PostSharp
Message Field '<Field>' must not be read-only because its declaring type is [PSerializable].

Cause

[PSerializableAttribute] serializes every instance field of the type, except the fields marked [PNonSerializedAttribute] and the fields of a delegate type. When PortableFormatter deserializes the object, the generated serializer first creates the instance, then assigns each of these fields. A readonly field can be assigned only in a constructor, so PostSharp reports SR0004 for the first read-only field that it would serialize.

The field can be a field that you wrote, or the backing field that C# generates for a get-only auto-property such as public string Name { get; }. In the second case, <Field> shows the name of the backing field, which contains the name of the property.

PostSharp reports the message on the type, not on the field. In the following example, the lines field of Order is read-only.

using System;
using System.Collections.Generic;
using System.IO;
using PostSharp.Serialization;

namespace Errors.SR0004_Error;

[PSerializable]
public class Order // The lines field of Order is read-only.
{
    private readonly List<string> lines = new();

    public void AddLine( string product ) => this.lines.Add( product );

    public IReadOnlyList<string> Lines => this.lines;
}

public static class Program
{
    public static void Main()
    {
        var order = new Order();
        order.AddLine( "Keyboard" );
        order.AddLine( "Mouse" );

        var formatter = new PortableFormatter();
        var stream = new MemoryStream();
        formatter.Serialize( order, stream );
        stream.Position = 0;

        var copy = (Order) formatter.Deserialize( stream );
        Console.WriteLine( $"Order with {string.Join( ", ", copy.Lines )}" );
    }
}
error SR0004: SR0004_Error.cs(9,14): Field 'Errors.SR0004_Error.Order.lines' must not be read-only because its declaring type is [PSerializable].

Resolution

  • Remove the readonly modifier of the field. For a get-only auto-property, add a setter, which can be private.
  • When the field does not need to be serialized, for example because it is computed from other fields, mark it with [PNonSerializedAttribute]. In the following example, the lines field is no longer read-only, and an Order is serialized and deserialized with its lines.
using System;
using System.Collections.Generic;
using System.IO;
using PostSharp.Serialization;

namespace Errors.SR0004_Fixed;

[PSerializable]
public class Order
{
    // The deserializer assigns this field, so it cannot be read-only.
    private List<string> lines = new();

    public void AddLine( string product ) => this.lines.Add( product );

    public IReadOnlyList<string> Lines => this.lines;
}

public static class Program
{
    public static void Main()
    {
        var order = new Order();
        order.AddLine( "Keyboard" );
        order.AddLine( "Mouse" );

        var formatter = new PortableFormatter();
        var stream = new MemoryStream();
        formatter.Serialize( order, stream );
        stream.Position = 0;

        var copy = (Order) formatter.Deserialize( stream );
        Console.WriteLine( $"Order with {string.Join( ", ", copy.Lines )}" );
    }
}
Order with Keyboard, Mouse

See Also

Reference

PSerializableAttribute
PNonSerializedAttribute
PortableFormatter

Other Resources

Understanding Aspect Serialization