CA2240: Implement ISerializable correctly
Item | Value |
---|---|
RuleId | CA2240 |
Category | Microsoft.Usage |
Breaking change | Non-breaking |
Cause
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 System.Runtime.Serialization.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.
Rule description
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.
How to fix violations
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.
When to suppress warnings
Do not suppress a warning from this rule.
Example 1
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; }
}
}
}
Example 2
The following example fixes the two previous violations by providing an overrideable implementation of GetObjectData on the Book class and by providing an implementation of 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);
}
}
}
Related rules
- 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
- CA2239: Provide deserialization methods for optional fields
- CA2120: Secure serialization constructors