Last active
July 3, 2018 17:56
-
-
Save bbarry/75db9239ad9da9b58d9312be4e4601c9 to your computer and use it in GitHub Desktop.
This file contains 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
using System; | |
using System.Reflection; | |
using System.Runtime.CompilerServices; | |
using System.Runtime.Serialization; | |
namespace ConsoleApp2 | |
{ | |
class Program | |
{ | |
static void Main(string[] args) | |
{ | |
// name2 here could have been: | |
// var name2 = new Name("John", "Doe") { FirstName = "Frank" }; | |
// or | |
// var name2 = name with { FirstName = "Frank" }; | |
var name = new Name("John", "Doe"); | |
var b = Name.ToBuilder(name); | |
b.FirstName = "Frank"; | |
var name2 = b.Build(); | |
Console.WriteLine("{0} {1}", name.FirstName, name.LastName); // John Doe | |
Console.WriteLine("{0} {1}", name2.FirstName, name2.LastName); // Frank Doe | |
Console.WriteLine(); | |
// d could be: | |
// var d = new MyDataClass { Member1 = "test1", MemberN = "test2" }; | |
// or | |
// var d = temp with { Member1 = "test1", MemberN = "test2" }; | |
var temp = new MyDataClass(); | |
var temp2 = MyDataClass.ToBuilder(temp); | |
temp2.Member1 = "test1"; | |
temp2.MemberN = "test2"; | |
var d = temp2.Build(); | |
Console.WriteLine(temp); // original1 original2 originalN | |
Console.WriteLine(d); // test1 original2 test2 | |
Console.WriteLine(); | |
// demonstrating no decapitation: | |
var child = new MyDataChild(); | |
var e = NewMember2(child); | |
Console.WriteLine(e); // original1 foo original3 originalN | |
Console.ReadKey(); | |
} | |
private static MyDataClass NewMember2(MyDataClass child) | |
{ | |
// var e = child with { Member2 = "foo" }; | |
var temp3 = MyDataClass.ToBuilder(child); | |
temp3.Member2 = "foo"; | |
var e = temp3.Build(); | |
return e; | |
} | |
} | |
class MyDataChild : MyDataClass | |
{ | |
public string Member3 { get; } = "original3"; | |
public override string ToString() => $"{Member1} {Member2} {Member3} {MemberN}"; | |
} | |
class MyDataClass | |
{ | |
public string Member1 { get; } = "original1"; | |
public string Member2 { get; } = "original2"; | |
public string MemberN { get; } = "originalN"; | |
public static Builder ToBuilder(MyDataClass instance) | |
{ | |
var b = new Builder(instance); | |
Copier.Copy(b, instance); | |
return b; | |
} | |
public override string ToString() => $"{Member1} {Member2} {MemberN}"; | |
public class Builder | |
{ | |
private readonly MyDataClass _reference; | |
public Builder(MyDataClass reference) => _reference = reference; | |
public string Member1 { get; set; } | |
public string Member2 { get; set; } | |
public string MemberN { get; set; } | |
public MyDataClass Build() => Copier.Clone(this, _reference); | |
} | |
} | |
public static class Copier | |
{ | |
private const BindingFlags BindingFlags = System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.DeclaredOnly; | |
public static void Copy<TFrom, TTo>(TTo destination, TFrom source) | |
{ | |
for (var type = typeof(TFrom); type != null; type = type.BaseType) | |
{ | |
var fields = type.GetFields(BindingFlags); | |
foreach (var field in fields) | |
{ | |
var tofield = typeof(TTo).GetField(field.Name, BindingFlags); | |
tofield?.SetValue(destination, field.GetValue(source)); | |
} | |
} | |
} | |
public static TOriginal Clone<TOverrideData, TOriginal>(TOverrideData overrideData, TOriginal original) | |
{ | |
var runtimeType = original.GetType(); | |
var o = (TOriginal)FormatterServices.GetUninitializedObject(runtimeType); | |
for (var type = runtimeType; type != null; type = type.BaseType) | |
{ | |
var fields = type.GetFields(BindingFlags); | |
foreach (var field in fields) | |
{ | |
var overrideField = typeof(TOverrideData).GetField(field.Name, BindingFlags); | |
field.SetValue(o, overrideField == null ? field.GetValue(original) : overrideField.GetValue(overrideData)); | |
} | |
} | |
return o; | |
} | |
} | |
readonly struct Name | |
{ | |
public Name(string firstName, string lastName) | |
{ | |
FirstName = firstName; | |
LastName = lastName; | |
} | |
public string FirstName { get; } | |
public string LastName { get; } | |
public static Builder ToBuilder(in Name instance) => Unsafe.As<Name, Builder>(ref Unsafe.AsRef(instance)); | |
public struct Builder | |
{ | |
public string FirstName { get; set; } | |
public string LastName { get; set; } | |
public Name Build() => Unsafe.As<Builder, Name>(ref this); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment