Skip to content

Instantly share code, notes, and snippets.

@kjeske
Last active May 7, 2019 09:21
Show Gist options
  • Select an option

  • Save kjeske/da2bbbf8dc38e47e82186f9ab5735e2e to your computer and use it in GitHub Desktop.

Select an option

Save kjeske/da2bbbf8dc38e47e82186f9ab5735e2e to your computer and use it in GitHub Desktop.
Immutable changes in objects using AutoMapper
public static class ObjectExtensions
{
private static readonly ConcurrentDictionary<Type, Func<object, object>> MemberwiseCloneCache =
new ConcurrentDictionary<Type, Func<object, object>>();
public static T With<T>(this T obj, Action<T> mutator) where T : class
{
if (obj == null)
{
return null;
}
var cloneFunc = GetMemberwiseCloneFunc<T>();
if (cloneFunc == null)
{
return obj;
}
var clone = (T) cloneFunc(obj);
mutator(clone);
return clone;
}
private static Func<object, object> GetMemberwiseCloneFunc<T>() where T : class
{
var type = typeof(T);
if (MemberwiseCloneCache.ContainsKey(type))
{
return MemberwiseCloneCache[type];
}
var methodInfo = type.GetMethod("MemberwiseClone", BindingFlags.Instance | BindingFlags.NonPublic);
if (methodInfo == null)
{
return null;
}
var delegateFunc = (Func<object, object>)Delegate.CreateDelegate(typeof(Func<object, object>), methodInfo);
MemberwiseCloneCache.TryAdd(type, delegateFunc);
return delegateFunc;
}
}
var invoice = new Invoice { Customer = "John", Number = "10" };
var changedInvoice = invoice.With(i => i.Number = "20"); // change only Number property leaving the rest
Assert.NotEqual(invoice, changedInvoice);
Assert.Equal(invoice.Customer, "John");
Assert.Equal(invoice.Number, "10");
Assert.Equal(changedInvoice.Customer, "John");
Assert.Equal(changedInvoice.Number, "20");
// "With" method will perform a shallow copy of a given object, similar to Object.assign function in JavaScript
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment