CA2240: Implement ISerializable correctly
TypeName | ImplementISerializableCorrectly |
CheckId | CA2240 |
Category | Microsoft.Usage |
Breaking Change | Non Breaking |
An externally visible type is assignable to the System.Runtime.Serialization.ISerializable interface and one of the following conditions is true:
The type inherits but does not override the ISerializable.GetObjectData method and the type declares instance fields that are not marked with the System.NonSerializedAttribute attribute.
The type is not sealed and the type implements a GetObjectData method that is not externally visible and overridable.
Instance fields that are declared in a type that inherits the System.Runtime.Serialization.ISerializable interface are not automatically included in the serialization process. To include the fields, the type must implement the GetObjectData method and the serialization constructor. If the fields should not be serialized, apply the NonSerializedAttribute attribute to the fields to explicitly indicate the decision.
In types that are not sealed, implementations of the GetObjectData method should be externally visible. Therefore, the method can be called by derived types, and is overridable.
To fix a violation of this rule, make the GetObjectData method visible and overridable and make sure all instance fields are included in the serialization process or explicitly marked with the NonSerializedAttribute attribute.
The following example shows two serializable types that violate the rule.
using System; using System.Security.Permissions; using System.Runtime.Serialization; namespace Samples1 { // Violates this rule [Serializable] public class Book : ISerializable { private readonly string _Text; public Book(string text) { if (text == null) throw new ArgumentNullException("text"); _Text = text; } protected Book(SerializationInfo info, StreamingContext context) { if (info == null) throw new ArgumentNullException("info"); _Text = info.GetString("Text"); } public string Text { get { return _Text; } } [SecurityPermission(SecurityAction.Demand, SerializationFormatter = true)] public void GetObjectData(SerializationInfo info, StreamingContext context) { if (info == null) throw new ArgumentNullException("info"); info.AddValue("Text", _Text); } } // Violates this rule [Serializable] public class LibraryBook : Book { private readonly DateTime _CheckedOut; public LibraryBook(string text, DateTime checkedOut) : base(text) { _CheckedOut = checkedOut; } public DateTime CheckedOut { get { return _CheckedOut; } } } }
The following example fixes the two previous violations by providing an overrideable implementation of [ISerializable.GetObjectData] on the Book class and by providing an implementation of [ISerializable.GetObjectData] on the Library class.
using System; using System.Security.Permissions; using System.Runtime.Serialization; namespace Samples2 { [Serializable] public class Book : ISerializable { private readonly string _Title; public Book(string title) { if (title == null) throw new ArgumentNullException("title"); _Title = title; } protected Book(SerializationInfo info, StreamingContext context) { if (info == null) throw new ArgumentNullException("info"); _Title = info.GetString("Title"); } public string Title { get { return _Title; } } [SecurityPermission(SecurityAction.Demand, SerializationFormatter = true)] protected virtual void GetObjectData(SerializationInfo info, StreamingContext context) { info.AddValue("Title", _Title); } [SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.SerializationFormatter)] void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context) { if (info == null) throw new ArgumentNullException("info"); GetObjectData(info, context); } } [Serializable] public class LibraryBook : Book { private readonly DateTime _CheckedOut; public LibraryBook(string title, DateTime checkedOut) : base(title) { _CheckedOut = checkedOut; } protected LibraryBook(SerializationInfo info, StreamingContext context) : base(info, context) { _CheckedOut = info.GetDateTime("CheckedOut"); } public DateTime CheckedOut { get { return _CheckedOut; } } [SecurityPermission(SecurityAction.Demand, SerializationFormatter = true)] protected override void GetObjectData(SerializationInfo info, StreamingContext context) { base.GetObjectData(info, context); info.AddValue("CheckedOut", _CheckedOut); } } }
CA2236: Call base class methods on ISerializable types
CA2229: Implement serialization constructors
CA2238: Implement serialization methods correctly
CA2235: Mark all non-serializable fields
CA2237: Mark ISerializable types with SerializableAttribute