How to custom deserialize null values into C# Nullable<T> using the BinaryFormatter?
<p>I have an old legacy type which basically does the same thing as the C# nullable introduced in C# 2.0. And I have old persisted data containing that legacy type serialized with the binary formatter. I would like to remove the legacy type completely (and replace it with the C# nullable) and handle all required type conversion in the deserialization of the persisted data.</p>

<p>I have almost managed to do this using serialization binders and surrogates. However. I can not figure out how to get my legacy null values to deserialize into proper null values of the C# nullable type, instead of getting a null value I get an uninitialized object of the underlying (non-nullable) type.</p>

<p>The below sample will print out the following, but I want null instead of 0 on the last line:</p>

<pre><code>Before serialization: 17
Before serialization: &lt;null&gt;
After serialization: 17
After serialization: 0
</code></pre>

<p>The type I want to replace in the following sample is NullableInt which acts as a wrapper around the non-nullable Int class (the latter is used for creating strongly typed integers, but that is not important for my question).</p>

<pre class="lang-cs prettyprint-override"><code>using System;
using System.IO;
using System.Linq;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;

namespace ConsoleApplication7 {
    /// &lt;summary&gt;
    /// Non-nullable typed integer. We use these for creating strongly typed
    /// integers to make it harder to mix up integers having different
    /// purpose (like mixing up Int&lt;PatientId&gt; with Int&lt;ExaminationId&gt;).
    /// &lt;/summary&gt;
    [Serializable]
    internal struct Int&lt;T&gt; {

        public int Inner;

        public Int(int inner) {
            this.Inner = inner;
        }
    }

    /// &lt;summary&gt;
    /// Used as generic type argument to the typed integer
    /// &lt;/summary&gt;
    internal struct PatientId {
    }

    /// &lt;summary&gt;
    /// Nullable typed integer. This is the legacy type I want to 
    /// replace with the C# nullable, i.e. Int&lt;T&gt;?
    /// &lt;/summary&gt;
    [Serializable]
    internal struct NullableInt&lt;T&gt; {

        private object inner;
        public static NullableInt&lt;T&gt; Null = new NullableInt&lt;T&gt;();

        public Int&lt;T&gt; Value { get { return new Int&lt;T&gt;((int)inner); } }
        public bool IsNull { get { return (inner == null); } }

        public NullableInt(Int&lt;T&gt; value) {
            this.inner = value.Inner;
        }
    }

    /// &lt;summary&gt;
    /// SerializationBinder used during deserialization. It will map the legacy
    /// type to the C# nullable type.
    /// &lt;/summary&gt;
    internal class Binder : SerializationBinder {
        public override Type BindToType(string assemblyName, string typeName) {
            if (typeName == typeof(NullableInt&lt;PatientId&gt;).FullName) {
                return typeof(Int&lt;PatientId&gt;?);
            } else {
                throw new NotSupportedException();
            }
        }
    }

    /// &lt;summary&gt;
    /// SerializationSurrogate converting the legacy binary format
    /// of NullableInt&lt;T&gt; to C# nullable
    /// &lt;/summary&gt;
    internal class Surrogate&lt;T&gt; : ISerializationSurrogate {
        public object SetObjectData(object obj, SerializationInfo info, StreamingContext context, ISurrogateSelector selector) {
            // Get the object serialized by NullableInt&lt;T&gt;
            object inner = info.GetValue("inner", typeof(object));

            // Always return Int&lt;T&gt;?
            Int&lt;T&gt;? nullable = (inner == null) ? (Int&lt;T&gt;?)null : new Int&lt;T&gt;((int)inner);
            return nullable;
        }
        public void GetObjectData(object obj, SerializationInfo info, StreamingContext context) {
            throw new NotImplementedException();
        }
    }

    /// &lt;summary&gt;
    /// Main program testing serialization and deserialization
    /// &lt;/summary&gt;
    class Program {
        static void Main(string[] args) {
            // Create data to serialize
            var data = new NullableInt&lt;PatientId&gt;[2] {                
                new NullableInt&lt;PatientId&gt;(new Int&lt;PatientId&gt;(17)),
                NullableInt&lt;PatientId&gt;.Null
            };
            data.ToList().ForEach(x =&gt; Console.WriteLine(
                "Before serialization: " + (x.IsNull ? "&lt;null&gt;" : x.Value.Inner.ToString())));

            // Serialize into memory stream
            var formatter = new BinaryFormatter();
            var ms = new MemoryStream();
            formatter.Serialize(ms, data);

            // Set up binder for deserialization
            formatter = new BinaryFormatter();
            formatter.Binder = new Binder();

            // Setup surrogate for deserialization
            var surrogateSelector = new SurrogateSelector();
            surrogateSelector.AddSurrogate(
                typeof(Int&lt;PatientId&gt;), 
                new StreamingContext(StreamingContextStates.All), 
                new Surrogate&lt;PatientId&gt;());
            surrogateSelector.AddSurrogate(
                typeof(Int&lt;PatientId&gt;?), 
                new StreamingContext(StreamingContextStates.All), 
                new Surrogate&lt;PatientId&gt;());
            formatter.SurrogateSelector = surrogateSelector;

            // Deserialize
            ms.Seek(0, SeekOrigin.Begin);            
            var deserialized = (Int&lt;PatientId&gt;?[])formatter.Deserialize(ms);
            deserialized.ToList().ForEach(x =&gt; Console.WriteLine(
                "After serialization: " + (x.HasValue ? x.Value.Inner.ToString() : "&lt;null&gt;")));
        }
    }
}
</code></pre>

<p>Notice at the end that I do get an array of nullables back, the last one just is not null (but instead an uninitialized Int with inner value 0).</p>

<p>Two additional observations:</p>

<ol>
<li>If I remove the 1st surrogate, i.e. the first call to AddSurrogate, the ISerializationSurrogate does not get called at all</li>
<li>If I remove the 2nd surrogate, the ISerializationSurrogate does get called but with the SerializationInfo argument always set to null.</li>
</ol>

<p>Does anyone know what to return from the serialization surrogate to make the binary formatter return a null value instead of an uninitialized value? Or is there another way of doing what I want to do? I have also played around with implementing the IObjectReference interface, but without any luck.</p>