Last active
May 7, 2019 09:21
-
-
Save kjeske/da2bbbf8dc38e47e82186f9ab5735e2e to your computer and use it in GitHub Desktop.
Immutable changes in objects using AutoMapper
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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; | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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