Open sandboxFocusImprove this doc

SR0008: Serializer of the Base Type Does Not Derive from ReferenceTypeSerializer

Severity Error
Reported by PostSharp
Message Cannot apply [PSerializable] to a type derived from '<BaseType>' because the serializer '<Serializer>' of '<BaseType>' does not derive from ReferenceTypeSerializer.

Cause

When a [PSerializableAttribute] class derives from a class whose serializer is written by hand, the serializer that PostSharp generates for the derived class derives from the serializer of the base class. It overrides the methods that serialize and deserialize the fields, and calls the implementations of the base serializer for the fields of the base class. The serializer of a class must therefore derive from ReferenceTypeSerializer.

PostSharp reports SR0008 on <BaseType> when its serializer <Serializer> does not derive from ReferenceTypeSerializer. PostSharp takes as the serializer of <BaseType> the type given by its [SerializerAttribute] or, when it has no such attribute, its nested class named Serializer, whatever this class is.

In the following example, the serializer of Entity implements the ISerializer interface instead of deriving from ReferenceTypeSerializer. PostSharp also reports AR0101, because only the serializer classes of PostSharp may implement ISerializer.

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

namespace Errors.SR0008_Error;

[Serializer( typeof(EntitySerializer) )]
public class Entity // EntitySerializer does not derive from ReferenceTypeSerializer.
{
    public int Id { get; set; }

    public Entity() { }

    // The deserializing constructor, which the serializer of Customer calls.
    protected Entity( PortableFormatterConstructorContext context ) { }

    protected class EntitySerializer : ISerializer
    {
        public bool IsTwoPhase => true;

        public object Convert( object value, Type targetType ) => value;

        public object CreateInstance( Type type, IArgumentsReader constructorArguments ) => new Entity();

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

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

[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}" );
    }
}
error SR0008: SR0008_Error.cs(8,14): Cannot apply [PSerializable] to a type derived from 'Errors.SR0008_Error.Entity' because the serializer 'Errors.SR0008_Error.Entity.EntitySerializer' of 'Errors.SR0008_Error.Entity' does not derive from ReferenceTypeSerializer.
warning AR0101: SR0008_Error.cs(17,21): Interface PostSharp.Serialization.ISerializer cannot be implemented by Errors.SR0008_Error.Entity+EntitySerializer because of the [InternalImplement] constraint.

Resolution

  • Derive the serializer of the base type from ReferenceTypeSerializer.
  • When the base type has a nested class named Serializer that is not its serializer, give the base type its real serializer with [SerializerAttribute], which takes precedence over the nested class.
  • Apply [PSerializableAttribute] to the base type instead of writing its serializer by hand.

In the following example, EntitySerializer derives from ReferenceTypeSerializer, and a Customer is serialized and deserialized with its Id and its Name.

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

namespace Errors.SR0008_Fixed;

[Serializer( typeof(EntitySerializer) )]
public class Entity
{
    public int Id { get; set; }

    public Entity() { }

    // The deserializing constructor, which the serializer of Customer calls.
    protected Entity( PortableFormatterConstructorContext context ) { }

    // Derives from ReferenceTypeSerializer, which the serializer of Customer can extend.
    protected class EntitySerializer : 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
{
    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
ReferenceTypeSerializer
SerializerAttribute
ISerializer

Other Resources

Understanding Aspect Serialization