using System; using System.Linq; using System.Reflection; using System.Linq.Expressions; using System.Collections.Generic; using System.Collections.Concurrent; public static class AnonymousObjectMutator { private const BindingFlags FieldFlags = BindingFlags.NonPublic | BindingFlags.Instance; private const BindingFlags PropFlags = BindingFlags.Public | BindingFlags.Instance; private static readonly string[] BackingFieldFormats = { "<{0}>i__Field", "<{0}>" }; private static ConcurrentDictionary>> _map = new ConcurrentDictionary>>(); public static T Set( this T instance, Expression> propExpression, TProperty newValue) where T : class { GetSetterFor(propExpression)(instance, newValue); return instance; } private static Action GetSetterFor(Expression> propExpression) { var memberExpression = propExpression.Body as MemberExpression; if (memberExpression == null || memberExpression.Member.MemberType != MemberTypes.Property) throw new InvalidOperationException("Only property expressions are supported"); Action setter = null; GetPropMap().TryGetValue(memberExpression.Member.Name, out setter); if (setter == null) throw new InvalidOperationException("No setter found"); return setter; } private static IDictionary> GetPropMap() { return _map.GetOrAdd(typeof(T), x => BuildPropMap()); } private static IDictionary> BuildPropMap() { var typeMap = new Dictionary>(); var fields = typeof(T).GetFields(FieldFlags); foreach (var pi in typeof(T).GetProperties(PropFlags)) { var backingFieldNames = BackingFieldFormats.Select(x => string.Format(x, pi.Name)).ToList(); var fi = fields.FirstOrDefault(f => backingFieldNames.Contains(f.Name) && f.FieldType == pi.PropertyType); if (fi == null) throw new NotSupportedException(string.Format("No backing field found for property {0}.", pi.Name)); typeMap.Add(pi.Name, (inst, val) => fi.SetValue(inst, val)); } return typeMap; } } public class Program { public static void Main() { var myAnonInstance = new { FirstField = "Hello", AnotherField = 30, }; Console.WriteLine(myAnonInstance); myAnonInstance .Set(x => x.FirstField, "Hello SO") .Set(x => x.AnotherField, 42); Console.WriteLine(myAnonInstance); } }