Open sandboxFocusImprove this doc

SR0002: Base Type Has No Deserializing Constructor

Severity Error
Reported by PostSharp
Message Cannot apply [PSerializable] to type '<Type>' because its base type does not have a deserializing constructor, which takes a single parameter of type PortableFormatterConstructorContext. Apply [PSerializable] to the base type.

Cause

[PSerializableAttribute] generates a deserializing constructor in <Type>, which PortableFormatter calls to create the instance before it restores its fields. This constructor must call a constructor of the base type: the deserializing constructor of the base type, which has a single parameter of type PortableFormatterConstructorContext. [PSerializableAttribute] generates this constructor in the base type too, but a base type whose serializer is written by hand usually does not have it.

PostSharp reports SR0002 when the base type has a serializer, either a nested class named Serializer or a type given by [SerializerAttribute], but no deserializing constructor. When the base type is marked [Serializer(null)], so that it has no serializer, PostSharp also accepts its parameterless constructor, and reports SR0002 only when the base type has neither.

The base types object, ValueType, Attribute and MarshalByRefObject do not need a deserializing constructor. A base type that has no serializer at all is reported with SR0006 instead.

In the following example, Entity has a serializer written by hand, and Customer, derived from Entity, is [PSerializableAttribute].

using System;
using System.IO;
using PostSharp.Serialization;

namespace Errors.SR0002_Error;

public class Entity
{
    public int Id { get; set; }

    // A serializer written by hand, which does not use a deserializing constructor.
    public class Serializer : ReferenceTypeSerializer
    {
        public override object CreateInstance( Type type, IArgumentsReader constructorArguments ) => new Entity();

        public override void SerializeObject( object obj, IArgumentsWriter constructorArguments, IArgumentsWriter initializationArguments )
            => initializationArguments.SetValue( "Id", ((Entity) obj).Id );

        public override void DeserializeFields( object obj, IArgumentsReader initializationArguments )
            => ((Entity) obj).Id = initializationArguments.GetValue<int>( "Id" );
    }
}

[PSerializable]
public class Customer : Entity // Entity has no deserializing constructor.
{
    public string? Name { get; set; }
}

public static class Program
{
    public static void Main()
    {
        var formatter = new PortableFormatter();
        var stream = new MemoryStream();
        formatter.Serialize( new Customer { Id = 1, Name = "Alice" }, stream );
        stream.Position = 0;

        var customer = (Customer) formatter.Deserialize( stream );
        Console.WriteLine( $"Customer {customer.Id}: {customer.Name}" );
    }
}
error SR0002: SR0002_Error.cs(25,14): Cannot apply [PSerializable] to type 'Errors.SR0002_Error.Customer' because its base type does not have a deserializing constructor, which takes a single parameter of type PortableFormatterConstructorContext. Apply [PSerializable] to the base type.

Resolution

  • Apply [PSerializableAttribute] to the base type instead of writing its serializer by hand. PostSharp then generates its serializer and its deserializing constructor, and the serializer of the derived type extends the one of the base type.
  • When the base type has no state to serialize, mark it with [Serializer(null)] and give it a parameterless constructor that the derived type can access.

In the following example, Entity is [PSerializableAttribute], and a Customer is serialized and deserialized with its Id and its Name.

using System;
using System.IO;
using PostSharp.Serialization;

namespace Errors.SR0002_Fixed;

// PostSharp generates the serializer and the deserializing constructor of the base class too.
[PSerializable]
public class Entity
{
    public int Id { get; set; }
}

[PSerializable]
public class Customer : Entity
{
    public string? Name { get; set; }
}

public static class Program
{
    public static void Main()
    {
        var formatter = new PortableFormatter();
        var stream = new MemoryStream();
        formatter.Serialize( new Customer { Id = 1, Name = "Alice" }, stream );
        stream.Position = 0;

        var customer = (Customer) formatter.Deserialize( stream );
        Console.WriteLine( $"Customer {customer.Id}: {customer.Name}" );
    }
}
Customer 1: Alice

See Also

Reference

PSerializableAttribute
SerializerAttribute
PortableFormatter

Other Resources

Understanding Aspect Serialization