private void EmitObjectConverter(ILGenerator il, Type inType, Type outType)
        {
            var outProps = outType.GetProperties();
            var inProps  = inType.GetProperties();

            var casted     = il.DeclareLocal(inType);
            var converted  = il.DeclareLocal(outType);
            var v2         = il.DeclareLocal(outType);
            var v3         = il.DeclareLocal(objectType);
            var brLabel    = il.DefineLabel();
            var returnType = !outType.IsInterface ? outType : inType;

            //var casted = (IModel1<...>) @in;
            il.Ldarg(1);
            il.Castclass(inType);
            il.Stloc(casted);

            //return new Model1<...>
            var returnConstructor = returnType.GetConstructor(Type.EmptyTypes);

            il.Newobj(returnConstructor);
            il.Stloc(v2);

            for (var i = 0; i < outProps.Length; i++)
            {
                var inProp  = inProps[i];
                var outProp = outProps[i];

                if (!outType.IsInterface && inProp.PropertyType != outProp.PropertyType)
                {
                    //OutProp = (OutPropType)ConverterStorage.Instance.GetImplementation(casted.InProp.GetType(), casted.OutProp.GetType()).Convert(casted.InProp)
                    il.Ldloc(v2);
                    il.Ldsfld(ConverterStorage.InstanceFieldInfo);
                    il.Ldtoken(inProp.PropertyType);
                    il.Call(typeOfMethodInfo);
                    il.Ldtoken(outProp.PropertyType);
                    il.Call(typeOfMethodInfo);
                    il.Callvirt(ConverterStorage.GetImplementationFieldInfo);
                    il.Ldloc(casted);
                    il.Callvirt(inProp.GetMethod);
                    il.Callvirt(convertMethodInfo);
                    il.Castclass(outProp.PropertyType);
                    il.Callvirt(outProp.SetMethod);

                    continue;
                }

                //Prop1 = casted.Prop1,
                il.Ldloc(v2);
                il.Ldloc(casted);
                il.Callvirt(inProp.GetMethod);
                il.Callvirt(!outType.IsInterface ? outProp.SetMethod : inProp.SetMethod);
            }

            il.Ldloc(v2);
            il.Stloc(converted);
            il.Ldloc(converted);
            il.Stloc(v3);
            il.BrS(brLabel);
            il.MarkLabel(brLabel);
            il.Ldloc(v3);
            il.Ret();
        }
        private void EmitStructConverter(ILGenerator il, Type inType, Type outType)
        {
            var casted    = il.DeclareLocal(inType);
            var converted = il.DeclareLocal(outType);
            var v2        = il.DeclareLocal(objectType);

            var inPropMap    = inType.GetProperties().ToDictionary(x => x.Name.ToLower());
            var outCtor      = outType.GetConstructors().OrderByDescending(x => x.GetParameters().Length).First();
            var ctorParamMap = outCtor
                               .GetParameters()
                               .Select(outParam =>
            {
                var outParamKey = outParam.Name.ToLower();
                return(new { outParam, inProp = inPropMap.ContainsKey(outParamKey) ? inPropMap[outParamKey] : null });
            })
                               .ToArray();

            var invalidParamTransitions = ctorParamMap.Where(x => x.inProp == null).ToArray();

            if (invalidParamTransitions.Length > 0)
            {
                throw new Exception($"Can't serialize this kind of struct. Need to know how to map next ctor params: {string.Join(", ", invalidParamTransitions.Select(x => x.outParam.Name))}");
            }


            il.Ldarg(1);
            il.Unbox_Any(inType);
            il.Stloc(casted);

            il.Ldloca(converted);

            foreach (var ctorParamPair in ctorParamMap)
            {
                var inItem      = ctorParamPair.inProp;
                var outItem     = ctorParamPair.outParam;
                var inItemType  = inItem.PropertyType;
                var outItemType = outItem.ParameterType;
                if (inItemType == outItemType)
                {
                    il.Ldloca(casted);
                    il.Call(inItem.GetMethod);
                }
                else
                {
                    il.Ldsfld(ConverterStorage.InstanceFieldInfo);
                    il.Ldtoken(inItemType);
                    il.Call(typeOfMethodInfo);
                    il.Ldtoken(outItemType);
                    il.Call(typeOfMethodInfo);
                    il.Callvirt(ConverterStorage.GetImplementationFieldInfo);
                    il.Ldloca(casted);
                    il.Call(inItem.GetMethod);
                    il.Callvirt(convertMethodInfo);
                    il.Castclass(outItemType);
                }
            }

            il.Call(outCtor);

            il.Ldloc(converted);
            il.Box(outType);
            il.Stloc(v2);

            var brLabel = il.DefineLabel();

            il.BrS(brLabel);
            il.MarkLabel(brLabel);

            il.Ldloc(v2);
            il.Ret();
        }
        private void EmitCollectionConverter(ILGenerator il, Type inType, Type outType)
        {
            if ((inType.IsIEnumerableInterface() || inType.IsIEnumerableInterfaceImplementation()) && outType.IsArray)
            {
                var inItemType  = inType.GetEnumerableType();
                var outItemType = outType.GetElementType();

                if (inItemType != outItemType)
                {
                    var casted    = il.DeclareLocal(inType);
                    var converted = il.DeclareLocal(outType);
                    var v2        = il.DeclareLocal(objectType);

                    il.Ldarg(1);
                    il.Castclass(inType);
                    il.Stloc(casted);

                    il.Ldloc(casted);
                    il.Call(collectionConverterInfo.MakeGenericMethod(inItemType, outItemType));
                    il.Stloc(converted);

                    il.Ldloc(converted);
                    il.Stloc(v2);

                    var ret = il.DefineLabel();
                    il.BrS(ret);
                    il.MarkLabel(ret);
                    il.Ldloc(v2);
                    il.Ret();
                }
                else
                {
                    var casted = il.DeclareLocal(inType);
                    var array  = il.DeclareLocal(inItemType.MakeArrayType());
                    var v2     = il.DeclareLocal(objectType);

                    il.Ldarg(1);
                    il.Castclass(inType);
                    il.Stloc(casted);

                    il.Ldloc(casted);
                    il.Call(typeof(Enumerable).GetMethod("ToArray").MakeGenericMethod(inItemType));
                    il.Stloc(array);

                    il.Ldloc(array);
                    il.Stloc(v2);

                    var ret = il.DefineLabel();
                    il.BrS(ret);
                    il.MarkLabel(ret);
                    il.Ldloc(v2);
                    il.Ret();
                }
            }
            else
            {
                var inItemType  = inType.GetElementType();
                var outItemType = outType.GetEnumerableType();

                if (inItemType != outItemType)
                {
                    var casted    = il.DeclareLocal(typeof(IEnumerable <>).MakeGenericType(inItemType));
                    var converted = il.DeclareLocal(outType);
                    var v2        = il.DeclareLocal(objectType);

                    il.Ldarga(1);
                    il.Castclass(inType);
                    il.Stloc(casted);

                    il.Ldloc(casted);
                    il.Call(collectionConverterInfo.MakeGenericMethod(inItemType, outItemType));
                    il.Stloc(converted);

                    il.Ldloc(converted);
                    il.Stloc(v2);

                    var ret = il.DefineLabel();
                    il.BrS(ret);
                    il.MarkLabel(ret);
                    il.Ldloc(v2);
                    il.Ret();
                }
                else
                {
                    var v0 = il.DeclareLocal(objectType);

                    il.Ldarg(1);
                    il.Castclass(inType);
                    il.Stloc(v0);

                    var ret = il.DefineLabel();
                    il.BrS(ret);
                    il.MarkLabel(ret);
                    il.Ldloc(v0);
                    il.Ret();
                }
            }
        }