Ejemplo n.º 1
0
        public SerializerTypeDescriptor(Type type, HashSet <Type> previousTypes = null)
        {
            Type = type;
            if (previousTypes == null)
            {
                previousTypes = new HashSet <Type>();
            }
            var ifaces            = type.GetInterfaces();
            var iListType         = ifaces.FirstOrDefault(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IList <>));
            var iDictionaryType   = ifaces.FirstOrDefault(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IDictionary <,>));
            var isIList           = iListType != null;
            var isIDictionary     = iDictionaryType != null;
            var runtimeProperties = type.GetRuntimeProperties().OrderBy(p => p.Name).Where(prop =>
            {
                if (prop.IsSpecialName || !prop.CanRead || !prop.CanWrite)
                {
                    return(false);
                }
                if (prop.GetAttribute <NonSerializeAttribute>() != null)
                {
                    return(false);
                }
                if (prop.GetIndexParameters().Length > 0)
                {
                    return(false);
                }
                if (isIList && prop.Name == "Capacity")
                {
                    return(false);
                }
                return(true);
            }).ToArray();

            //
            var properties = runtimeProperties.Select(p => p.Name).ToArray();

            IsRawSerializable = ifaces.Any(i => i == typeof(IRawSerializable));
            var isArray = type.IsArray;
            var isList  = false;

            if (!isArray)
            {
                isList = !isIDictionary && isIList;
            }
            //
            var typeName       = type.GetTypeName();
            var defText        = typeName + ";" + (isArray ? "1" : "0") + ";" + (isList ? "1" : "0") + ";" + (isIDictionary ? "1" : "0") + ";" + properties.Join(";");
            var defBytesLength = Encoding.UTF8.GetByteCount(defText);
            var defBytes       = new byte[defBytesLength + 5];

            defBytes[0] = DataBytesDefinition.TypeStart;
            defBytes[1] = (byte)defBytesLength;
            defBytes[2] = (byte)(defBytesLength >> 8);
            defBytes[3] = (byte)(defBytesLength >> 16);
            defBytes[4] = (byte)(defBytesLength >> 24);
            Encoding.UTF8.GetBytes(defText, 0, defText.Length, defBytes, 5);
            Definition      = defBytes;
            SerializeAction = null;
            //
            var serExpressions = new List <Expression>();
            var varExpressions = new List <ParameterExpression>();

            //
            var obj      = Expression.Parameter(typeof(object), "obj");
            var serTable = Expression.Parameter(typeof(SerializersTable), "table");

            var instance = Expression.Parameter(type, "instance");

            varExpressions.Add(instance);
            serExpressions.Add(Expression.Assign(instance, Expression.Convert(obj, type)));
            //
            if (isArray)
            {
                var elementType = type.GetElementType();

                var arrLength = Expression.Parameter(typeof(int), "length");
                varExpressions.Add(arrLength);
                serExpressions.Add(Expression.Assign(arrLength, Expression.Call(instance, SerializersTable.ArrayLengthGetMethod)));
                serExpressions.Add(Expression.Call(serTable, SerializersTable.WriteIntMethodInfo, arrLength));

                var forIdx = Expression.Parameter(typeof(int), "i");
                varExpressions.Add(forIdx);
                var breakLabel = Expression.Label(typeof(void), "exitLoop");
                serExpressions.Add(Expression.Assign(forIdx, Expression.Constant(0)));

                var getValueExpression = Expression.ArrayIndex(instance, Expression.PostIncrementAssign(forIdx));

                var loop = Expression.Loop(
                    Expression.IfThenElse(
                        Expression.LessThan(forIdx, arrLength),
                        WriteExpression(elementType, getValueExpression, serTable),
                        Expression.Break(breakLabel)), breakLabel);
                serExpressions.Add(loop);
            }
            else if (isList)
            {
                var argTypes = iListType.GenericTypeArguments;
                var iLength  = Expression.Parameter(typeof(int), "length");
                varExpressions.Add(iLength);
                serExpressions.Add(Expression.Assign(iLength, Expression.Call(instance, SerializersTable.ListCountGetMethod)));
                serExpressions.Add(Expression.Call(serTable, SerializersTable.WriteIntMethodInfo, iLength));

                var forIdx = Expression.Parameter(typeof(int), "i");
                varExpressions.Add(forIdx);
                var breakLabel = Expression.Label(typeof(void), "exitLoop");
                serExpressions.Add(Expression.Assign(forIdx, Expression.Constant(0)));

                var getValueExpression = Expression.MakeIndex(instance, SerializersTable.ListIndexProperty, new[] { Expression.PostIncrementAssign(forIdx) });

                var loop = Expression.Loop(
                    Expression.IfThenElse(
                        Expression.LessThan(forIdx, iLength),
                        WriteExpression(argTypes[0], getValueExpression, serTable),
                        Expression.Break(breakLabel)), breakLabel);
                serExpressions.Add(loop);
            }
            else if (isIDictionary)
            {
                var argTypes = iDictionaryType.GenericTypeArguments;
                var iLength  = Expression.Parameter(typeof(int), "length");
                varExpressions.Add(iLength);
                serExpressions.Add(Expression.Assign(iLength, Expression.Call(instance, SerializersTable.ListCountGetMethod)));
                serExpressions.Add(Expression.Call(serTable, SerializersTable.WriteIntMethodInfo, iLength));

                var enumerator = Expression.Parameter(typeof(IDictionaryEnumerator), "enumerator");
                varExpressions.Add(enumerator);
                serExpressions.Add(Expression.Assign(enumerator, Expression.Call(instance, SerializersTable.DictionaryGetEnumeratorMethod)));

                var breakLabel         = Expression.Label(typeof(void), "exitLoop");
                var getKeyExpression   = Expression.Convert(Expression.Call(enumerator, SerializersTable.DictionaryEnumeratorKeyMethod), argTypes[0]);
                var getValueExpression = Expression.Convert(Expression.Call(enumerator, SerializersTable.DictionaryEnumeratorValueMethod), argTypes[1]);

                var loop = Expression.Loop(
                    Expression.IfThenElse(
                        Expression.Call(enumerator, SerializersTable.EnumeratorMoveNextMethod),
                        Expression.Block(
                            WriteExpression(argTypes[0], getKeyExpression, serTable),
                            WriteExpression(argTypes[1], getValueExpression, serTable)
                            ),
                        Expression.Break(breakLabel)), breakLabel);

                serExpressions.Add(loop);
            }
            //
            if (runtimeProperties.Length > 0)
            {
                foreach (var prop in runtimeProperties)
                {
                    var getExpression = Expression.Property(instance, prop);
                    serExpressions.Add(WriteExpression(prop.PropertyType, getExpression, serTable));
                }
            }
            //
            var serExpression = Expression.Block(varExpressions, serExpressions).Reduce();

            var innerValue         = Expression.Parameter(type, "innerValue");
            var innerSerExpression = Expression.Block(varExpressions, new[] { Expression.Assign(instance, innerValue) }.Concat(serExpressions.Skip(1))).Reduce();

            InnerWriterLambda = Expression.Lambda(innerSerExpression, type.Name + "Writer", new[] { innerValue, serTable });

            var serializationLambda = Expression.Lambda <SerializeActionDelegate>(serExpression, type.Name + "_Serializer", new[] { obj, serTable });

            SerializeAction = serializationLambda.Compile();

            Expression WriteExpression(Type itemType, Expression itemGetExpression, ParameterExpression serTableExpression)
            {
                if (itemType == typeof(int))
                {
                    return(SerializersTable.WriteIntExpression(itemGetExpression, serTableExpression));
                }
                if (itemType == typeof(int?))
                {
                    return(SerializersTable.WriteNulleableIntExpression(itemGetExpression, serTableExpression));
                }
                if (itemType == typeof(bool))
                {
                    return(SerializersTable.WriteBooleanExpression(itemGetExpression, serTableExpression));
                }
                if (itemType == typeof(bool?))
                {
                    return(SerializersTable.WriteNulleableBooleanExpression(itemGetExpression, serTableExpression));
                }
                if (SerializersTable.WriteValues.TryGetValue(itemType, out var wMethodTuple))
                {
                    return(Expression.Call(serTableExpression, wMethodTuple.Method, itemGetExpression));
                }
                if (itemType.IsEnum)
                {
                    return(Expression.Call(serTableExpression, SerializersTable.WriteValues[typeof(Enum)].Method, Expression.Convert(itemGetExpression, typeof(Enum))));
                }
                if (itemType.IsGenericType && itemType.GetGenericTypeDefinition() == typeof(Nullable <>))
                {
                    var nullParam = Expression.Parameter(itemType);
                    var nullBlock = Expression.Block(new[] { nullParam },
                                                     Expression.Assign(nullParam, itemGetExpression),
                                                     Expression.IfThenElse(
                                                         Expression.Equal(nullParam, Expression.Constant(null, itemType)),
                                                         Expression.Call(serTable, SerializersTable.WriteByteMethodInfo, Expression.Constant(DataBytesDefinition.ValueNull)),
                                                         WriteExpression(itemType.GenericTypeArguments[0], nullParam, serTableExpression)
                                                         )
                                                     );
                    return(nullBlock);
                }
                if (itemType == typeof(IEnumerable) || itemType == typeof(IEnumerable <>) || itemType.ReflectedType == typeof(IEnumerable) || itemType.ReflectedType == typeof(IEnumerable <>) ||
                    itemType.ReflectedType == typeof(Enumerable) || itemType.FullName.IndexOf("System.Linq", StringComparison.Ordinal) > -1 ||
                    itemType.IsAssignableFrom(typeof(IEnumerable)))
                {
                    return(Expression.Call(serTableExpression, SerializersTable.InternalWriteObjectValueMInfo, itemGetExpression));
                }
                if (itemType.IsAbstract || itemType.IsInterface || itemType == typeof(object))
                {
                    return(Expression.Call(serTableExpression, SerializersTable.InternalWriteObjectValueMInfo, itemGetExpression));
                }
                if (itemType.IsSealed)
                {
                    return(WriteSealedExpression(itemType, itemGetExpression, serTableExpression));
                }

                return(WriteMixedExpression(itemType, itemGetExpression, serTableExpression));

                //return Expression.Call(serTableExpression, SerializersTable.InternalSimpleWriteObjectValueMInfo, itemGetExpression);
            }

            Expression WriteSealedExpression(Type itemType, Expression itemGetExpression, ParameterExpression serTableExpression)
            {
                if (itemType == type)
                {
                    return(Expression.Call(serTableExpression, SerializersTable.InternalSimpleWriteObjectValueMInfo, itemGetExpression));
                }
                if (!previousTypes.Add(itemType))
                {
                    return(Expression.Call(serTableExpression, SerializersTable.InternalSimpleWriteObjectValueMInfo, itemGetExpression));
                }

                var innerDescriptor = SerializersTable.Descriptors.GetOrAdd(itemType, (tp, ptypes) => new SerializerTypeDescriptor(tp, ptypes), previousTypes);

                var innerVarExpressions = new List <ParameterExpression>();
                var innerSerExpressions = new List <Expression>();

                var returnLabel = Expression.Label(typeof(void), "return");

                var value = Expression.Parameter(itemType, "value");

                innerVarExpressions.Add(value);
                innerSerExpressions.Add(Expression.Assign(value, Expression.Convert(itemGetExpression, itemType)));

                #region Null Check
                innerSerExpressions.Add(
                    Expression.IfThen(Expression.ReferenceEqual(value, Expression.Constant(null)),
                                      Expression.Block(
                                          Expression.Call(serTable, SerializersTable.WriteByteMethodInfo, Expression.Constant(DataBytesDefinition.ValueNull)),
                                          Expression.Return(returnLabel)
                                          )
                                      )
                    );
                #endregion

                #region Object Cache Check
                var objCacheExp = Expression.Field(serTableExpression, "_objectCache");
                var objIdxExp   = Expression.Variable(typeof(int), "objIdx");

                innerVarExpressions.Add(objIdxExp);

                innerSerExpressions.Add(
                    Expression.IfThenElse(Expression.Call(objCacheExp, SerializersTable.TryGetValueObjectSerializerCacheMethod, value, objIdxExp),
                                          Expression.Block(
                                              Expression.Call(serTable, SerializersTable.WriteRefObjectMInfo, objIdxExp),
                                              Expression.Return(returnLabel)
                                              ),
                                          Expression.Call(objCacheExp, SerializersTable.SetObjectSerializerCacheMethod, value)
                                          )
                    );
                #endregion

                #region Type Cache Check
                var typeCacheExp = Expression.Field(serTableExpression, "_typeCache");
                var typeIdxExp   = Expression.Variable(typeof(int), "tIdx");

                innerVarExpressions.Add(typeIdxExp);

                var descDefinition = typeof(SerializerTypeDescriptor <>).MakeGenericType(itemType).GetField("Definition", BindingFlags.Public | BindingFlags.Static);

                innerSerExpressions.Add(
                    Expression.IfThenElse(Expression.Call(typeCacheExp, SerializersTable.TryGetValueTypeSerializerCacheMethod, Expression.Constant(itemType, typeof(Type)), typeIdxExp),
                                          Expression.Call(serTable, SerializersTable.WriteRefTypeMInfo, typeIdxExp),
                                          Expression.Block(
                                              Expression.Call(serTable, SerializersTable.WriteBytesMethodInfo, Expression.Field(null, descDefinition)),
                                              Expression.Call(typeCacheExp, SerializersTable.SetTypeSerializerCacheMethod, Expression.Constant(itemType, typeof(Type)))
                                              )
                                          )
                    );

                #endregion

                #region Serializer Call
                if (innerDescriptor.IsRawSerializable)
                {
                    innerSerExpressions.Add(Expression.Call(Expression.Convert(value, typeof(IRawSerializable)), RawSerializableSerializeMInfo, serTableExpression));
                }
                else
                {
                    innerSerExpressions.Add(Expression.Invoke(innerDescriptor.InnerWriterLambda, value, serTable));
                }
                #endregion

                innerSerExpressions.Add(Expression.Call(serTable, SerializersTable.WriteByteMethodInfo, Expression.Constant(DataBytesDefinition.TypeEnd)));
                innerSerExpressions.Add(Expression.Label(returnLabel));
                var innerExpression = Expression.Block(innerVarExpressions, innerSerExpressions).Reduce();

                previousTypes.Remove(itemType);
                return(innerExpression);
            }

            Expression WriteMixedExpression(Type itemType, Expression itemGetExpression, ParameterExpression serTableExpression)
            {
                if (itemType == type)
                {
                    return(Expression.Call(serTableExpression, SerializersTable.InternalSimpleWriteObjectValueMInfo, itemGetExpression));
                }
                if (!previousTypes.Add(itemType))
                {
                    return(Expression.Call(serTableExpression, SerializersTable.InternalSimpleWriteObjectValueMInfo, itemGetExpression));
                }

                var innerDescriptor = SerializersTable.Descriptors.GetOrAdd(itemType, (tp, ptypes) => new SerializerTypeDescriptor(tp, ptypes), previousTypes);

                var innerVarExpressions = new List <ParameterExpression>();
                var innerSerExpressions = new List <Expression>();

                var returnLabel = Expression.Label(typeof(void), "return");
                var objValue    = Expression.Parameter(typeof(object), "objValue");

                innerVarExpressions.Add(objValue);
                innerSerExpressions.Add(Expression.Assign(objValue, itemGetExpression));

                #region Null Check
                innerSerExpressions.Add(
                    Expression.IfThen(Expression.ReferenceEqual(objValue, Expression.Constant(null)),
                                      Expression.Block(
                                          Expression.Call(serTable, SerializersTable.WriteByteMethodInfo, Expression.Constant(DataBytesDefinition.ValueNull)),
                                          Expression.Return(returnLabel)
                                          )
                                      )
                    );
                #endregion

                #region Object Cache Check
                var objCacheExp = Expression.Field(serTableExpression, "_objectCache");
                var objIdxExp   = Expression.Variable(typeof(int), "objIdx");

                innerVarExpressions.Add(objIdxExp);

                innerSerExpressions.Add(
                    Expression.IfThenElse(Expression.Call(objCacheExp, SerializersTable.TryGetValueObjectSerializerCacheMethod, objValue, objIdxExp),
                                          Expression.Block(
                                              Expression.Call(serTable, SerializersTable.WriteRefObjectMInfo, objIdxExp),
                                              Expression.Return(returnLabel)
                                              ),
                                          Expression.Call(objCacheExp, SerializersTable.SetObjectSerializerCacheMethod, objValue)
                                          )
                    );
                #endregion


                var valueType = Expression.Parameter(typeof(Type), "valueType");
                innerVarExpressions.Add(valueType);
                innerSerExpressions.Add(Expression.Assign(valueType, Expression.Call(objValue, SerializersTable.GetTypeMethodInfo)));

                innerSerExpressions.Add(
                    Expression.IfThenElse(Expression.TypeIs(objValue, itemType),
                                          WriteShortSealedExpression(itemType, objValue, serTableExpression, returnLabel, innerDescriptor),
                                          Expression.Call(serTableExpression, SerializersTable.InternalMixedWriteObjectValueMInfo, objValue, valueType)
                                          )
                    );

                innerSerExpressions.Add(Expression.Call(serTable, SerializersTable.WriteByteMethodInfo, Expression.Constant(DataBytesDefinition.TypeEnd)));
                innerSerExpressions.Add(Expression.Label(returnLabel));
                var innerExpression = Expression.Block(innerVarExpressions, innerSerExpressions).Reduce();

                previousTypes.Remove(itemType);
                return(innerExpression);
            }

            Expression WriteShortSealedExpression(Type itemType, Expression itemGetExpression, ParameterExpression serTableExpression, LabelTarget returnLabel, SerializerTypeDescriptor innerDescriptor)
            {
                var innerVarExpressions = new List <ParameterExpression>();
                var innerSerExpressions = new List <Expression>();


                var value = Expression.Parameter(itemType, "value");

                innerVarExpressions.Add(value);
                innerSerExpressions.Add(Expression.Assign(value, Expression.Convert(itemGetExpression, itemType)));

                #region Type Cache Check
                var typeCacheExp = Expression.Field(serTableExpression, "_typeCache");
                var typeIdxExp   = Expression.Variable(typeof(int), "tIdx");

                innerVarExpressions.Add(typeIdxExp);

                var descDefinition = typeof(SerializerTypeDescriptor <>).MakeGenericType(itemType).GetField("Definition", BindingFlags.Public | BindingFlags.Static);

                innerSerExpressions.Add(
                    Expression.IfThenElse(Expression.Call(typeCacheExp, SerializersTable.TryGetValueTypeSerializerCacheMethod, Expression.Constant(itemType, typeof(Type)), typeIdxExp),
                                          Expression.Call(serTable, SerializersTable.WriteRefTypeMInfo, typeIdxExp),
                                          Expression.Block(
                                              Expression.Call(serTable, SerializersTable.WriteBytesMethodInfo, Expression.Field(null, descDefinition)),
                                              Expression.Call(typeCacheExp, SerializersTable.SetTypeSerializerCacheMethod, Expression.Constant(itemType, typeof(Type)))
                                              )
                                          )
                    );

                #endregion

                #region Serializer Call
                if (innerDescriptor.IsRawSerializable)
                {
                    innerSerExpressions.Add(Expression.Call(Expression.Convert(value, typeof(IRawSerializable)), RawSerializableSerializeMInfo, serTableExpression));
                }
                else
                {
                    innerSerExpressions.Add(Expression.Invoke(innerDescriptor.InnerWriterLambda, value, serTable));
                }
                #endregion

                var innerExpression = Expression.Block(innerVarExpressions, innerSerExpressions).Reduce();

                return(innerExpression);
            }
        }
Ejemplo n.º 2
0
        public SerializerTypeDescriptor(Type type)
        {
            var ifaces            = type.GetInterfaces();
            var iListType         = ifaces.FirstOrDefault(i => i.GetTypeInfo().IsGenericType&& i.GetGenericTypeDefinition() == typeof(IList <>));
            var iDictionaryType   = ifaces.FirstOrDefault(i => i.GetTypeInfo().IsGenericType&& i.GetGenericTypeDefinition() == typeof(IDictionary <,>));
            var isIList           = iListType != null;
            var isIDictionary     = iDictionaryType != null;
            var runtimeProperties = type.GetRuntimeProperties().OrderBy(p => p.Name).Where(prop =>
            {
                if (prop.IsSpecialName || !prop.CanRead || !prop.CanWrite)
                {
                    return(false);
                }
                if (prop.GetAttribute <NonSerializeAttribute>() != null)
                {
                    return(false);
                }
                if (prop.GetIndexParameters().Length > 0)
                {
                    return(false);
                }
                if (isIList && prop.Name == "Capacity")
                {
                    return(false);
                }
                return(true);
            }).ToArray();

            //
            Properties      = runtimeProperties.Select(p => p.Name).ToArray();
            IsNSerializable = ifaces.Any(i => i == typeof(INSerializable));
            IsArray         = type.IsArray;
            if (!IsArray)
            {
                IsDictionary = isIDictionary;
                IsList       = !IsDictionary && isIList;
            }
            else
            {
                IsList       = false;
                IsDictionary = false;
            }
            //
            var typeName       = type.GetTypeName();
            var defText        = typeName + ";" + (IsArray ? "1" : "0") + ";" + (IsList ? "1" : "0") + ";" + (IsDictionary ? "1" : "0") + ";" + Properties.Join(";");
            var defBytesLength = Encoding.UTF8.GetByteCount(defText);
            var defBytes       = new byte[defBytesLength + 5];

            defBytes[0] = DataBytesDefinition.TypeStart;
            defBytes[1] = (byte)defBytesLength;
            defBytes[2] = (byte)(defBytesLength >> 8);
            defBytes[3] = (byte)(defBytesLength >> 16);
            defBytes[4] = (byte)(defBytesLength >> 24);
            Encoding.UTF8.GetBytes(defText, 0, defText.Length, defBytes, 5);
            Definition = defBytes;

            //
            var serExpressions = new List <Expression>();
            var varExpressions = new List <ParameterExpression>();
            //
            var obj      = Expression.Parameter(typeof(object), "obj");
            var serTable = Expression.Parameter(typeof(SerializersTable), "table");

            var instance = Expression.Parameter(type, "instance");

            varExpressions.Add(instance);
            serExpressions.Add(Expression.Assign(instance, Expression.Convert(obj, type)));

            if (IsArray)
            {
                //var elementType = iListType.GetElementType();
                //var itemMethod = !type. ?
                //    SerializersTable.InternalWriteObjectValueMInfo :
                //    SerializersTable.WriteValues.TryGetValue(elementType, out var wMethodTuple) ? wMethodTuple.Method : SerializersTable.InternalWriteObjectValueMInfo;


                var elementType    = type.GetElementType();
                var itemSpecMethod = false;
                var itemMethod     = SerializersTable.InternalWriteObjectValueMInfo;
                if (SerializersTable.WriteValues.TryGetValue(elementType, out var propMethod))
                {
                    itemMethod     = propMethod.Method;
                    itemSpecMethod = true;
                }
                else if (elementType.IsEnum)
                {
                    itemMethod     = SerializersTable.WriteValues[typeof(Enum)].Method;
                    itemSpecMethod = true;
                }

                var arrLength = Expression.Parameter(typeof(int), "length");
                varExpressions.Add(arrLength);
                serExpressions.Add(Expression.Assign(arrLength, Expression.Call(instance, SerializersTable.ArrayLengthGetMethod)));
                serExpressions.Add(Expression.Call(serTable, SerializersTable.WriteIntMethodInfo, arrLength));

                var forIdx = Expression.Parameter(typeof(int), "i");
                varExpressions.Add(forIdx);
                var breakLabel = Expression.Label(typeof(void), "exitLoop");
                serExpressions.Add(Expression.Assign(forIdx, Expression.Constant(0)));
                var loop = Expression.Loop(
                    Expression.IfThenElse(
                        Expression.LessThan(forIdx, arrLength),
                        itemSpecMethod ?
                        elementType.IsEnum ?
                        Expression.Call(serTable, itemMethod, Expression.Convert(Expression.ArrayIndex(instance, Expression.PostIncrementAssign(forIdx)), typeof(Enum))) :
                        Expression.Call(serTable, itemMethod, Expression.Convert(Expression.ArrayIndex(instance, Expression.PostIncrementAssign(forIdx)), elementType)) :
                        Expression.Call(serTable, itemMethod, Expression.ArrayIndex(instance, Expression.PostIncrementAssign(forIdx))),
                        Expression.Break(breakLabel)), breakLabel);
                serExpressions.Add(loop);
            }
            else if (IsList)
            {
                var argTypes   = iListType.GenericTypeArguments;
                var itemMethod = !type.IsGenericType ?
                                 SerializersTable.InternalWriteObjectValueMInfo :
                                 SerializersTable.WriteValues.TryGetValue(argTypes[0], out var wMethodTuple) ? wMethodTuple.Method : SerializersTable.InternalWriteObjectValueMInfo;

                var iLength = Expression.Parameter(typeof(int), "length");
                varExpressions.Add(iLength);
                serExpressions.Add(Expression.Assign(iLength, Expression.Call(instance, SerializersTable.ListCountGetMethod)));
                serExpressions.Add(Expression.Call(serTable, SerializersTable.WriteIntMethodInfo, iLength));

                var forIdx = Expression.Parameter(typeof(int), "i");
                varExpressions.Add(forIdx);
                var breakLabel = Expression.Label(typeof(void), "exitLoop");
                serExpressions.Add(Expression.Assign(forIdx, Expression.Constant(0)));
                var loop = Expression.Loop(
                    Expression.IfThenElse(
                        Expression.LessThan(forIdx, iLength),
                        Expression.Call(serTable, itemMethod, Expression.MakeIndex(instance, SerializersTable.ListIndexProperty, new[] { Expression.PostIncrementAssign(forIdx) })),
                        Expression.Break(breakLabel)), breakLabel);
                serExpressions.Add(loop);
            }
            else if (IsDictionary)
            {
                var argTypes  = iDictionaryType.GenericTypeArguments;
                var keyMember = !type.IsGenericType ?
                                SerializersTable.InternalWriteObjectValueMInfo :
                                SerializersTable.WriteValues.TryGetValue(argTypes[0], out var wMethodTuple) ? wMethodTuple.Method : SerializersTable.InternalWriteObjectValueMInfo;
                var valueMember = !type.IsGenericType ?
                                  SerializersTable.InternalWriteObjectValueMInfo :
                                  SerializersTable.WriteValues.TryGetValue(argTypes[1], out var wMethodTuple1) ? wMethodTuple1.Method : SerializersTable.InternalWriteObjectValueMInfo;

                var iLength = Expression.Parameter(typeof(int), "length");
                varExpressions.Add(iLength);
                serExpressions.Add(Expression.Assign(iLength, Expression.Call(instance, SerializersTable.ListCountGetMethod)));
                serExpressions.Add(Expression.Call(serTable, SerializersTable.WriteIntMethodInfo, iLength));

                var enumerator = Expression.Parameter(typeof(IDictionaryEnumerator), "enumerator");
                varExpressions.Add(enumerator);
                serExpressions.Add(Expression.Assign(enumerator, Expression.Call(instance, SerializersTable.DictionaryGetEnumeratorMethod)));

                var breakLabel = Expression.Label(typeof(void), "exitLoop");

                var loop = Expression.Loop(
                    Expression.IfThenElse(
                        Expression.Call(enumerator, SerializersTable.EnumeratorMoveNextMethod),
                        Expression.Block(
                            Expression.Call(serTable, keyMember, Expression.Convert(Expression.Call(enumerator, SerializersTable.DictionaryEnumeratorKeyMethod), argTypes[0])),
                            Expression.Call(serTable, valueMember, Expression.Convert(Expression.Call(enumerator, SerializersTable.DictionaryEnumeratorValueMethod), argTypes[1]))
                            ).Reduce(),
                        Expression.Break(breakLabel)), breakLabel);

                serExpressions.Add(loop);
            }

            //
            if (runtimeProperties.Length > 0)
            {
                foreach (var prop in runtimeProperties)
                {
                    var getMethod     = prop.GetMethod;
                    var getExpression = Expression.Call(instance, getMethod);
                    if (SerializersTable.WriteValues.TryGetValue(prop.PropertyType, out var wMethodTuple))
                    {
                        serExpressions.Add(Expression.Call(serTable, wMethodTuple.Method, getExpression));
                    }
                    else if (prop.PropertyType.IsEnum)
                    {
                        serExpressions.Add(Expression.Call(serTable, SerializersTable.WriteValues[typeof(Enum)].Method, Expression.Convert(getExpression, typeof(Enum))));
                    }
                    else
                    {
                        serExpressions.Add(Expression.Call(serTable, SerializersTable.InternalWriteObjectValueMInfo, getExpression));
                    }
                }
            }

            var expressionBlock = Expression.Block(varExpressions, serExpressions).Reduce();
            var lambda          = Expression.Lambda <SerializeActionDelegate>(expressionBlock, type.Name + "_Serializer", new[] { obj, serTable });

            SerializeAction = lambda.Compile();
        }