| Severity | Error |
| Reported by | PostSharp |
| Message | Cannot apply [PSerializable] to a type derived from '<BaseType>' because the serializer '<Serializer>' of '<BaseType>' does not have a default constructor. |
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. The constructor of the generated serializer has no parameter, and calls the constructor of the base serializer that has no parameter either.
PostSharp reports SR0010 on <BaseType> when its serializer <Serializer> has no parameterless constructor, for example because its only constructor takes an option. A parameterless constructor that exists but cannot be accessed from the generated serializer is reported with SR0011 instead.
In the following example, the only constructor of EntitySerializer takes the name under which the Id is serialized.
using System;
using System.IO;
using PostSharp.Serialization;
namespace Errors.SR0010_Error;
[Serializer( typeof(EntitySerializer) )]
public class Entity // EntitySerializer has no default constructor.
{
public int Id { get; set; }
public Entity() { }
// The deserializing constructor, which the serializer of Customer calls.
protected Entity( PortableFormatterConstructorContext context ) { }
// A serializer written by hand, which the serializer generated for Customer extends.
protected class EntitySerializer : ReferenceTypeSerializer
{
private readonly string idKey;
public EntitySerializer( string idKey )
{
this.idKey = idKey;
}
public override object CreateInstance( Type type, IArgumentsReader constructorArguments ) => new Entity();
public override void SerializeObject( object obj, IArgumentsWriter constructorArguments, IArgumentsWriter initializationArguments )
=> initializationArguments.SetValue( this.idKey, ((Entity) obj).Id );
public override void DeserializeFields( object obj, IArgumentsReader initializationArguments )
=> ((Entity) obj).Id = initializationArguments.GetValue<int>( this.idKey );
}
}
[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 SR0010: SR0010_Error.cs(8,14): Cannot apply [PSerializable] to a type derived from 'Errors.SR0010_Error.Entity' because the serializer 'Errors.SR0010_Error.Entity.EntitySerializer' of 'Errors.SR0010_Error.Entity' does not have a default constructor.
Resolution
Add a parameterless constructor to the serializer of the base type, public or protected, which gives a default value to the options of the other constructors.
In the following example, the default constructor of EntitySerializer calls the other one with "Id", and a Customer is serialized and deserialized with its Id and its Name.
using System;
using System.IO;
using PostSharp.Serialization;
namespace Errors.SR0010_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 ) { }
// A serializer written by hand, which the serializer generated for Customer extends.
protected class EntitySerializer : ReferenceTypeSerializer
{
private readonly string idKey;
// The default constructor, which the serializer of Customer calls.
public EntitySerializer() : this( "Id" ) { }
public EntitySerializer( string idKey )
{
this.idKey = idKey;
}
public override object CreateInstance( Type type, IArgumentsReader constructorArguments ) => new Entity();
public override void SerializeObject( object obj, IArgumentsWriter constructorArguments, IArgumentsWriter initializationArguments )
=> initializationArguments.SetValue( this.idKey, ((Entity) obj).Id );
public override void DeserializeFields( object obj, IArgumentsReader initializationArguments )
=> ((Entity) obj).Id = initializationArguments.GetValue<int>( this.idKey );
}
}
[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
Other Resources