private ILDelegateBuilder <Deserializer> EmitDeserializer(Type type, List <FieldInfo> fields,
                                                                  SerializationCallbacks callbacks)
        {
            var il = new ILDelegateBuilder <Deserializer>(
                FieldBuilder,
                type.Name + "Deserializer",
                SerializationMethodInfos.DeserializerDelegate);

            var streamingContext = default(ILDelegateBuilder <Deserializer> .Local);

            if (callbacks.OnDeserializing != null || callbacks.OnDeserialized != null)
            {
                streamingContext = il.DeclareLocal(typeof(StreamingContext));
                il.LoadLocalAddress(streamingContext);
                il.LoadConstant((int)StreamingContextStates.All);
                il.LoadArgument(1);
                il.Call(typeof(StreamingContext).GetConstructor(new[]
                                                                { typeof(StreamingContextStates), typeof(object) }));
            }

            // Declare local variables.
            var result = il.DeclareLocal(type);

            // Construct the result.
            il.CreateInstance(type, result, SerializationMethodInfos.GetUninitializedObject);

            // Record the object.
            il.LoadArgument(1); // Load the 'context' parameter.
            il.LoadLocal(result);
            il.BoxIfValueType(type);
            il.Call(SerializationMethodInfos.RecordObjectWhileDeserializing);

            if (callbacks.OnDeserializing != null)
            {
                il.LoadLocalAsReference(type, result);
                il.LoadLocal(streamingContext);
                il.Call(callbacks.OnDeserializing);
            }

            // Deserialize each field.
            foreach (var field in fields)
            {
                // Deserialize the field.
                var fieldType = field.FieldType;
                if (fieldType.IsEnum)
                {
                    var typeHandle = fieldType.GetEnumUnderlyingType().TypeHandle;
                    il.LoadLocalAsReference(type, result);

                    il.LoadArgument(1);
                    il.Call(SerializationMethodInfos.GetStreamFromDeserializationContext);
                    il.Call(DirectSerializers[typeHandle].ReadMethod);
                    il.StoreField(field);
                }
                else if (DirectSerializers.TryGetValue(field.FieldType.TypeHandle, out var serializer))
                {
                    il.LoadLocalAsReference(type, result);
                    il.LoadArgument(1);
                    il.Call(SerializationMethodInfos.GetStreamFromDeserializationContext);
                    il.Call(serializer.ReadMethod);

                    il.StoreField(field);
                }
                else
                {
                    var deserializeMethod = SerializationMethodInfos.DeserializeInner;

                    il.LoadLocalAsReference(type, result);
                    il.LoadType(field.FieldType);
                    il.LoadArgument(1);
                    il.Call(deserializeMethod);

                    // Store the value on the result.
                    il.CastOrUnbox(field.FieldType);
                    il.StoreField(field);
                }
            }

            if (callbacks.OnDeserialized != null)
            {
                il.LoadLocalAsReference(type, result);
                il.LoadLocal(streamingContext);
                il.Call(callbacks.OnDeserialized);
            }

            // If the type implements the IOnDeserialized lifecycle handler, call that method now.
            if (typeof(IOnDeserialized).IsAssignableFrom(type))
            {
                il.LoadLocalAsReference(type, result);
                il.LoadArgument(1);
                var concreteMethod = GetConcreteMethod(
                    type,
                    TypeUtils.Method((IOnDeserialized i) => i.OnDeserialized(default(ISerializerContext))));
                il.Call(concreteMethod);
            }

            // If the type implements the IDeserializationCallback lifecycle handler, call that method now.
            if (typeof(IDeserializationCallback).IsAssignableFrom(type))
            {
                il.LoadLocalAsReference(type, result);
                il.LoadArgument(1);

                var concreteMethod = GetConcreteMethod(
                    type,
                    TypeUtils.Method((IDeserializationCallback i) => i.OnDeserialization(default(object))));
                il.Call(concreteMethod);
            }

            il.LoadLocal(result);
            il.BoxIfValueType(type);
            il.Return();
            return(il);
        }
        private ILDelegateBuilder <Serializer> EmitSerializer(Type type, List <FieldInfo> fields,
                                                              SerializationCallbacks callbacks)
        {
            var il = new ILDelegateBuilder <Serializer>(
                FieldBuilder,
                type.Name + "Serializer",
                SerializationMethodInfos.SerializerDelegate);

            // Declare local variables.
            var typedInput = il.DeclareLocal(type);

            var streamingContext = default(ILDelegateBuilder <Serializer> .Local);

            if (callbacks.OnSerializing != null || callbacks.OnSerialized != null)
            {
                streamingContext = il.DeclareLocal(typeof(StreamingContext));
                il.LoadLocalAddress(streamingContext);
                il.LoadConstant((int)StreamingContextStates.All);
                il.LoadArgument(1);
                il.Call(typeof(StreamingContext).GetConstructor(new[] { typeof(StreamingContextStates), typeof(object) }));
            }

            // Set the typed input variable from the method parameter.
            il.LoadArgument(0);
            il.CastOrUnbox(type);
            il.StoreLocal(typedInput);

            if (callbacks.OnSerializing != null)
            {
                il.LoadLocalAsReference(type, typedInput);
                il.LoadLocal(streamingContext);
                il.Call(callbacks.OnSerializing);
            }

            // Serialize each field
            foreach (var field in fields)
            {
                var fieldType  = field.FieldType;
                var typeHandle = field.FieldType.TypeHandle;
                if (fieldType.IsEnum)
                {
                    typeHandle = fieldType.GetEnumUnderlyingType().TypeHandle;
                }

                if (DirectSerializers.TryGetValue(typeHandle, out var serializer))
                {
                    il.LoadArgument(1);
                    il.Call(SerializationMethodInfos.GetStreamFromSerializationContext);
                    il.LoadLocal(typedInput);
                    il.LoadField(field);

                    il.Call(serializer.WriteMethod);
                }
                else
                {
                    var serializeMethod = SerializationMethodInfos.SerializeInner;

                    // Load the field.
                    il.LoadLocal(typedInput);
                    il.LoadField(field);

                    il.BoxIfValueType(field.FieldType);

                    // Serialize the field.
                    il.LoadArgument(1);
                    il.LoadType(field.FieldType);
                    il.Call(serializeMethod);
                }
            }

            if (callbacks.OnSerialized != null)
            {
                il.LoadLocalAsReference(type, typedInput);
                il.LoadLocal(streamingContext);
                il.Call(callbacks.OnSerialized);
            }

            il.Return();
            return(il);
        }