CA2240: Implement ISerializable correctly
The new home for Visual Studio documentation is Visual Studio 2017 Documentation on docs.microsoft.com.
The latest version of this topic can be found at 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.
Do not suppress a warning from this rule.
The following example shows two serializable types that violate the rule.
using namespace System; using namespace System::Security::Permissions; using namespace System::Runtime::Serialization; namespace Samples1 { // Violates this rule [Serializable] public ref class Book : ISerializable { private: initonly String^ _Title; public: Book(String^ title) { if (title == nullptr) throw gcnew ArgumentNullException("title"); _Title = title; } property String^ Title { String^ get() { return _Title; } } protected: Book(SerializationInfo^ info, StreamingContext context) { if (info == nullptr) throw gcnew ArgumentNullException("info"); _Title = info->GetString("Title"); } private: [SecurityPermission(SecurityAction::LinkDemand, Flags = SecurityPermissionFlag::SerializationFormatter)] void virtual GetObjectData(SerializationInfo^ info, StreamingContext context) sealed = ISerializable::GetObjectData { if (info == nullptr) throw gcnew ArgumentNullException("info"); info->AddValue("Title", _Title); } }; // Violates this rule [Serializable] public ref class LibraryBook : Book { initonly DateTime _CheckedOut; public: LibraryBook(String^ title, DateTime checkedOut) : Book(title) { _CheckedOut = checkedOut; } property DateTime CheckedOut { DateTime 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 namespace System; using namespace System::Security::Permissions; using namespace System::Runtime::Serialization; namespace Samples2 { [Serializable] public ref class Book : ISerializable { private: initonly String^ _Title; public: Book(String^ title) { if (title == nullptr) throw gcnew ArgumentNullException("title"); _Title = title; } property String^ Title { String^ get() { return _Title; } } protected: Book(SerializationInfo^ info, StreamingContext context) { if (info == nullptr) throw gcnew ArgumentNullException("info"); _Title = info->GetString("Title"); } [SecurityPermission(SecurityAction::LinkDemand, Flags = SecurityPermissionFlag::SerializationFormatter)] void virtual GetObjectData(SerializationInfo^ info, StreamingContext context) = ISerializable::GetObjectData { if (info == nullptr) throw gcnew ArgumentNullException("info"); info->AddValue("Title", _Title); } }; [Serializable] public ref class LibraryBook : Book { initonly DateTime _CheckedOut; public: LibraryBook(String^ title, DateTime checkedOut) : Book(title) { _CheckedOut = checkedOut; } property DateTime CheckedOut { DateTime get() { return _CheckedOut; } } protected: LibraryBook(SerializationInfo^ info, StreamingContext context) : Book(info, context) { _CheckedOut = info->GetDateTime("CheckedOut"); } [SecurityPermission(SecurityAction::LinkDemand, Flags = SecurityPermissionFlag::SerializationFormatter)] void virtual GetObjectData(SerializationInfo^ info, StreamingContext context) override { Book::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