public static Func <T, int, string> GenerateToString <T>() { var param = Expression.Parameter(typeof(T), "obj"); var indent = Expression.Parameter(typeof(int), "indentation"); var array = Expression.Parameter(typeof(string[]), "array"); var stringConcat2 = typeof(string).GetMethod("Concat", new Type[] { typeof(object), typeof(object) }); var stringConcat3 = typeof(string).GetMethod("Concat", new Type[] { typeof(object), typeof(object), typeof(object) }); var stringJoin = typeof(string).GetMethod("Join", new[] { typeof(string), typeof(string[]) }); var lineIndent = ((Func <int, string>)IlHelpers.GetRecordLineIndent).Method; var getStrings = GetFields <T>() .Select((fieldInfo, i) => // String concat StringConcat2( // "Name: " Expression.Constant(getFieldName(fieldInfo) + ": ", typeof(string)), // And the to string result getToStringCall(fieldInfo) )); var block = Expression.Block( new[] { array }, Expression.Assign(array, Expression.NewArrayInit(typeof(string), getStrings)), Expression.Call( stringConcat3, StringConcat2( Expression.Constant("{\n"), Expression.Call(lineIndent, indent) ), Expression.Call( stringJoin, StringConcat2( Expression.Constant(",\n"), Expression.Call(lineIndent, indent)), array), StringConcat3( Expression.Constant("\n"), Expression.Call(lineIndent, Expression.Subtract(indent, Expression.Constant(1, typeof(int)))), Expression.Constant("}", typeof(string)) ) )); return(Expression.Lambda <Func <T, int, string> >(block, param, indent).Compile()); /***** Nested methods *****/ string getFieldName(FieldInfo nfo) { if (nfo.CustomAttributes.Any(x => x.AttributeType == typeof(CompilerGeneratedAttribute))) { return(IlHelpers.StripBakingFieldName(nfo.Name)); } return(nfo.Name); } Expression getToStringCall(FieldInfo nfo) { var prettyToString = typeof(object).GetMethod("ToString", new[] { typeof(int) }); if (prettyToString != null) { return(Expression.Call( Expression.Field(param, nfo), prettyToString, Expression.Add(indent, Expression.Constant(1)))); } return(Expression.Call( Expression.Field(param, nfo), typeof(object).GetMethod("ToString", new Type[] { }))); } Expression StringConcat2(Expression a, Expression b) { return(Expression.Call( stringConcat2, a, b)); } Expression StringConcat3(Expression a, Expression b, Expression c) { return(Expression.Call( stringConcat3, a, b, c)); } }
internal static IReadOnlyDictionary <MemberInfo, Action <T, object> > GenerateRecordFieldsSetMap <T>() { var(props, allFields) = GetMembers <T>() .Where(x => x.MemberType == MemberTypes.Field || x.MemberType == MemberTypes.Property) .Partition(x => x.MemberType == MemberTypes.Property) .Do(x => ( x.trues.Cast <PropertyInfo>().ToArray(), x.falses.Cast <FieldInfo>().ToArray())); var(autoFields, realFields) = allFields .Partition(x => x.CustomAttributes.Any(y => y.AttributeType == typeof(CompilerGeneratedAttribute))) .Do(x => (x.trues.ToArray(), x.falses.ToArray())); var autoProps = autoFields .Join( props, x => IlHelpers.StripBakingFieldName(x.Name), x => x.Name, (x, y) => (prop: y, field: x)) .ToArray(); var realProps = props .Except(autoProps.Select(x => x.prop)) .Where(x => x.CanWrite) .ToArray(); var value = Expression.Parameter(typeof(object), "value"); var target = Expression.Parameter(typeof(T), "target"); var realFieldAssignments = realFields .Select(x => (member: (MemberInfo)x, expression: (Expression)MakeFieldAssign(target, value, x))); var autoPropsAssignments = autoProps .Select(x => (member: (MemberInfo)x.prop, expression: (Expression)MakeFieldAssign(target, value, x.field))); var realPropsAssignments = realProps .Select(x => (member: (MemberInfo)x, expression: (Expression)Expression.Assign( Expression.Property(target, x.SetMethod), value))); return (realFieldAssignments .Union(autoPropsAssignments) .Union(realPropsAssignments) .ToDictionary( x => x.member, x => Expression.Lambda <Action <T, object> >(x.expression, target, value).Compile())); }