/// <summary>
        /// Gets the reader with a specified generic method.
        /// </summary>
        private static PortableReflectiveReadAction GetReader(FieldInfo field, MethodInfo method,
                                                              params Type[] genericArgs)
        {
            Debug.Assert(field != null);
            Debug.Assert(field.DeclaringType != null);   // non-static

            if (genericArgs.Length == 0)
            {
                genericArgs = new[] { field.FieldType }
            }
            ;

            // Call IPortableReader method
            var        readerParam  = Expression.Parameter(typeof(IPortableReader));
            var        fldNameParam = Expression.Constant(PortableUtils.CleanFieldName(field.Name));
            var        readMethod   = method.MakeGenericMethod(genericArgs);
            Expression readExpr     = Expression.Call(readerParam, readMethod, fldNameParam);

            if (readMethod.ReturnType != field.FieldType)
            {
                readExpr = Expression.Convert(readExpr, field.FieldType);
            }

            // Assign field value
            var targetParam          = Expression.Parameter(typeof(object));
            var targetParamConverted = Expression.Convert(targetParam, field.DeclaringType);
            var assignExpr           = Expression.Call(DelegateConverter.GetWriteFieldMethod(field), targetParamConverted,
                                                       readExpr);

            // Compile and return
            return(Expression.Lambda <PortableReflectiveReadAction>(assignExpr, targetParam, readerParam).Compile());
        }
    }
        /// <summary>
        /// Gets the reader with a specified read action.
        /// </summary>
        private static PortableReflectiveReadAction GetReader <T>(FieldInfo field,
                                                                  Expression <Func <string, IPortableReader, T> > read)
        {
            Debug.Assert(field != null);
            Debug.Assert(field.DeclaringType != null);   // non-static

            // Call IPortableReader method
            var        readerParam  = Expression.Parameter(typeof(IPortableReader));
            var        fldNameParam = Expression.Constant(PortableUtils.CleanFieldName(field.Name));
            Expression readExpr     = Expression.Invoke(read, fldNameParam, readerParam);

            if (typeof(T) != field.FieldType)
            {
                readExpr = Expression.Convert(readExpr, field.FieldType);
            }

            // Assign field value
            var targetParam          = Expression.Parameter(typeof(object));
            var targetParamConverted = Expression.Convert(targetParam, field.DeclaringType);
            var assignExpr           = Expression.Call(DelegateConverter.GetWriteFieldMethod(field), targetParamConverted,
                                                       readExpr);

            // Compile and return
            return(Expression.Lambda <PortableReflectiveReadAction>(assignExpr, targetParam, readerParam).Compile());
        }
        /// <summary>
        /// Gets the writer with a specified generic method.
        /// </summary>
        private static PortableReflectiveWriteAction GetWriter(FieldInfo field, MethodInfo method,
                                                               params Type[] genericArgs)
        {
            Debug.Assert(field != null);
            Debug.Assert(field.DeclaringType != null);   // non-static

            if (genericArgs.Length == 0)
            {
                genericArgs = new[] { field.FieldType }
            }
            ;

            // Get field value
            var targetParam          = Expression.Parameter(typeof(object));
            var targetParamConverted = Expression.Convert(targetParam, field.DeclaringType);
            var fldExpr = Expression.Field(targetParamConverted, field);

            // Call IPortableWriter method
            var writerParam  = Expression.Parameter(typeof(IPortableWriter));
            var fldNameParam = Expression.Constant(PortableUtils.CleanFieldName(field.Name));
            var writeMethod  = method.MakeGenericMethod(genericArgs);
            var writeExpr    = Expression.Call(writerParam, writeMethod, fldNameParam, fldExpr);

            // Compile and return
            return(Expression.Lambda <PortableReflectiveWriteAction>(writeExpr, targetParam, writerParam).Compile());
        }
        /// <summary>
        /// Gets the reader with a specified write action.
        /// </summary>
        private static PortableReflectiveWriteAction GetWriter <T>(FieldInfo field,
                                                                   Expression <Action <string, IPortableWriter, T> > write,
                                                                   bool convertFieldValToObject = false)
        {
            Debug.Assert(field != null);
            Debug.Assert(field.DeclaringType != null);   // non-static

            // Get field value
            var        targetParam          = Expression.Parameter(typeof(object));
            var        targetParamConverted = Expression.Convert(targetParam, field.DeclaringType);
            Expression fldExpr = Expression.Field(targetParamConverted, field);

            if (convertFieldValToObject)
            {
                fldExpr = Expression.Convert(fldExpr, typeof(object));
            }

            // Call IPortableWriter method
            var writerParam  = Expression.Parameter(typeof(IPortableWriter));
            var fldNameParam = Expression.Constant(PortableUtils.CleanFieldName(field.Name));
            var writeExpr    = Expression.Invoke(write, fldNameParam, writerParam, fldExpr);

            // Compile and return
            return(Expression.Lambda <PortableReflectiveWriteAction>(writeExpr, targetParam, writerParam).Compile());
        }
        /// <summary>
        /// Compare two FieldInfo instances.
        /// </summary>
        private static int Compare(FieldInfo info1, FieldInfo info2)
        {
            string name1 = PortableUtils.CleanFieldName(info1.Name);
            string name2 = PortableUtils.CleanFieldName(info2.Name);

            return(string.Compare(name1, name2, StringComparison.OrdinalIgnoreCase));
        }
        /// <summary>Register type.</summary>
        /// <param name="type">Type.</param>
        /// <param name="typeId">Type ID.</param>
        /// <param name="converter">Name converter.</param>
        /// <param name="idMapper">ID mapper.</param>
        public void Register(Type type, int typeId, IPortableNameMapper converter,
                             IPortableIdMapper idMapper)
        {
            if (type.GetInterface(typeof(IPortableMarshalAware).Name) != null)
            {
                return;
            }

            List <FieldInfo> fields = new List <FieldInfo>();

            Type curType = type;

            while (curType != null)
            {
                foreach (FieldInfo field in curType.GetFields(Flags))
                {
                    if (!field.IsNotSerialized)
                    {
                        fields.Add(field);
                    }
                }

                curType = curType.BaseType;
            }

            IDictionary <int, string> idMap = new Dictionary <int, string>();

            foreach (FieldInfo field in fields)
            {
                string fieldName = PortableUtils.CleanFieldName(field.Name);

                int fieldId = PortableUtils.FieldId(typeId, fieldName, converter, idMapper);

                if (idMap.ContainsKey(fieldId))
                {
                    throw new PortableException("Conflicting field IDs [type=" +
                                                type.Name + ", field1=" + idMap[fieldId] + ", field2=" + fieldName +
                                                ", fieldId=" + fieldId + ']');
                }

                idMap[fieldId] = fieldName;
            }

            fields.Sort(Compare);

            Descriptor desc = new Descriptor(fields);

            _types[type] = desc;
        }