## Why is this an issue?

The `ISerializable` interface is the mechanism to control the type serialization process. If not implemented correctly this could result in an invalid serialization and hard-to-detect bugs.

This rule raises an issue on types that implement `ISerializable` without following the serialization pattern recommended by Microsoft.

Specifically, this rule checks for these problems:

- The `SerializableAttribute` attribute is missing.
- Non-serializable fields are not marked with the `NonSerializedAttribute` attribute.
- There is no serialization constructor.
- An unsealed type has a serialization constructor that is not `protected`.
- A sealed type has a serialization constructor that is not `private`.
- An unsealed type has an `ISerializable.GetObjectData` that is not both `public` and `virtual`.
- A derived type has a serialization constructor that does not call the `base` constructor.
- A derived type has an `ISerializable.GetObjectData` method that does not call the `base` method.
- A derived type has serializable fields but the `ISerializable.GetObjectData` method is not overridden.

Classes that inherit from `Exception` are implementing `ISerializable`. Make sure the `[Serializable]` attribute is used and that `ISerializable` is correctly implemented. Even if you don’t plan to explicitly serialize the object yourself, it might still require serialization, for instance when crossing the boundary of an `AppDomain`.

This rule only raises an issue on classes that indicate that they are interested in serialization (see the Exceptions section). That is to reduce noise because a lot of classes in the base class library are implementing `ISerializable`, including the following classes: `Exception`, `Uri`, `Hashtable`, `Dictionary<TKey,TValue>`, `DataSet`, `HttpWebRequest`, `Regex` `TreeNode`, and others. There is often no need to add serialization support in classes derived from these types.

### Exceptions

- Classes in test projects are not checked.
- Classes need to indicate that they are interested in serialization support by either
  1. Applying the `[Serializable]` attribute
  2. Having `ISerializable` in their base type list
  3. Declaring a serialization constructor

    [Serializable]                                                                                 // 1.
    public class SerializationOptIn_Attribute
    {
    }

    public class SerializationOptIn_Interface : ISerializable                                      // 2.
    {
    }

    public class SerializationOptIn_Constructor
    {
        protected SerializationOptIn_Constructor(SerializationInfo info, StreamingContext context) // 3.
        {
        }
    }

## How to fix it

Make sure to follow the recommended guidelines when implementing `ISerializable`.

### Code examples

#### Noncompliant code example

    public class Bar
    {
    }

    public class Foo : ISerializable // Noncompliant: serialization constructor is missing
                                     // Noncompliant: the [Serializable] attribute is missing
    {
        private readonly Bar bar; // Noncompliant: the field is not marked with [NonSerialized]
    }

    public sealed class SealedFoo : Foo
    {
        private int val; // Noncompliant: 'val' is serializable and GetObjectData is not overridden

        public SealedFoo()
        {
            // ...
        }

        public SealedFoo(SerializationInfo info, StreamingContext context) // Noncompliant: serialization constructor is not `private`
                                                                           // Noncompliant: serialization constructor does not call base constructor
        {
            // ...
        }
    }

    public class UnsealedFoo : Foo
    {
        public UnsealedFoo()
        {
            // ...
        }

        public UnsealedFoo(SerializationInfo info, StreamingContext context) // Noncompliant: serialization constructor is not `protected`
            : base(info, context)
        {
            // ...
        }

        protected void GetObjectData(SerializationInfo info, StreamingContext context) // Noncompliant: GetObjectData is not public virtual
        {
            // Noncompliant: does not call base.GetObjectData(info, context)
        }
    }

#### Compliant solution

    public class Bar
    {
    }

    [Serializable]
    public class Foo : ISerializable // Compliant: the class is marked with [Serializable]
    {
        [NonSerialized]
        private readonly Bar bar; // Compliant: the field is marked with [NonSerialized]

        public Foo()
        {
            // ...
        }

        protected Foo(SerializationInfo info, StreamingContext context) // Compliant: serialization constructor is present
        {
            // ...
        }

        public virtual void GetObjectData(SerializationInfo info, StreamingContext context)
        {
            // ...
        }
    }

    [Serializable]
    public sealed class SealedFoo : Foo
    {
        private int val; // Compliant: 'val' is serializable and GetObjectData is overridden

        public SealedFoo()
        {
            // ...
        }

        private SealedFoo(SerializationInfo info, StreamingContext context) // Compliant: serialization constructor is `private`
            : base(info, context) // Compliant: serialization constructor calls base constructor
        {
            // ...
        }

        public override void GetObjectData(SerializationInfo info, StreamingContext context)
        {
            base.GetObjectData(info, context);
            // ...
        }
    }

    [Serializable]
    public class UnsealedFoo : Foo
    {
        public UnsealedFoo()
        {
            // ...
        }

        protected UnsealedFoo(SerializationInfo info, StreamingContext context) // Compliant: serialization constructor is `protected`
            : base(info, context)
        {
            // ...
        }

        public virtual void GetObjectData(SerializationInfo info, StreamingContext context) // Compliant: GetObjectData is public virtual
        {
            base.GetObjectData(info, context); // Compliant: calls base.GetObjectData(info, context)
            // ...

        }
    }

## Resources

### Documentation

- Microsoft Learn - Serialization
- Microsoft Learn - `ISerializable` Interface
- Microsoft Learn - `SerializableAttribute` Class
- Microsoft Learn - `NonSerializedAttribute` Class
- Microsoft Learn - `ISerializable.GetObjectData` Method
- Microsoft Learn - `Exception` Class