Skip to content

Instantly share code, notes, and snippets.

@bbarry
Last active July 3, 2018 17:56
Show Gist options
  • Save bbarry/75db9239ad9da9b58d9312be4e4601c9 to your computer and use it in GitHub Desktop.
Save bbarry/75db9239ad9da9b58d9312be4e4601c9 to your computer and use it in GitHub Desktop.
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