From 500025090f94a116ab8e0079a1aed41c0063ebf0 Mon Sep 17 00:00:00 2001 From: Ellpeck Date: Sun, 25 Oct 2020 20:11:15 +0100 Subject: [PATCH] added a CopyConstructor attribute to CopyExtensions --- MLEM.Data/CopyExtensions.cs | 25 +++++++++++++++++++------ Sandbox/GameImpl.cs | 11 ++++++++--- 2 files changed, 27 insertions(+), 9 deletions(-) diff --git a/MLEM.Data/CopyExtensions.cs b/MLEM.Data/CopyExtensions.cs index e621c05..1c2064f 100644 --- a/MLEM.Data/CopyExtensions.cs +++ b/MLEM.Data/CopyExtensions.cs @@ -1,4 +1,5 @@ using System; +using System.Linq; using System.Reflection; namespace MLEM.Data { @@ -11,7 +12,7 @@ namespace MLEM.Data { /// /// Creates a shallow copy of the object and returns it. - /// Note that, for this to work correctly, needs to contain a parameterless constructor. + /// Note that, for this to work correctly, needs to contain a parameterless constructor or a constructor with the . /// /// The object to create a shallow copy of /// The binding flags for field searching @@ -26,7 +27,7 @@ namespace MLEM.Data { /// /// Creates a deep copy of the object and returns it. - /// Note that, for this to work correctly, needs to contain a parameterless constructor. + /// Note that, for this to work correctly, needs to contain a parameterless constructor or a constructor with the . /// /// The object to create a deep copy of /// The binding flags for field searching @@ -56,7 +57,7 @@ namespace MLEM.Data { /// /// Copies the given object into the given object in a deep manner. - /// Note that, for this to work correctly, each type that should be constructed below the topmost level needs to contanin a parameterless constructor. + /// Note that, for this to work correctly, each type that should be constructed below the topmost level needs to contanin a parameterless constructor or a constructor with the . /// /// The object to create a deep copy of /// The object to copy into @@ -84,11 +85,23 @@ namespace MLEM.Data { } private static object Construct(Type t, BindingFlags flags) { - var constructor = t.GetConstructor(flags, null, Type.EmptyTypes, null); + // find a contructor with the correct attribute + var constructor = t.GetConstructors().FirstOrDefault(c => c.GetCustomAttribute() != null); + // fall back to a parameterless constructor if (constructor == null) - throw new NullReferenceException($"Type {t} does not have a parameterless constructor with the required visibility"); - return constructor.Invoke(null); + constructor = t.GetConstructor(flags, null, Type.EmptyTypes, null); + if (constructor == null) + throw new NullReferenceException($"Type {t} does not have a parameterless constructor with the required visibility or a constructor with the CopyConstructorAttribute"); + return constructor.Invoke(new object[constructor.GetParameters().Length]); } } + + /// + /// An attribute that, when added to a constructor, will make that constructor the one used by , and . + /// + [AttributeUsage(AttributeTargets.Constructor)] + public class CopyConstructorAttribute : Attribute { + + } } \ No newline at end of file diff --git a/Sandbox/GameImpl.cs b/Sandbox/GameImpl.cs index ca23e65..57300dd 100644 --- a/Sandbox/GameImpl.cs +++ b/Sandbox/GameImpl.cs @@ -89,11 +89,11 @@ namespace Sandbox { panel.SetData("TestKey", new Vector2(10, 2)); //Console.WriteLine(panel.GetData("TestKey")); - var obj = new Test { + var obj = new Test(Vector2.One, "test") { Vec = new Vector2(10, 20), Point = new Point(20, 30), Dir = Direction2.Left, - OtherTest = new Test { + OtherTest = new Test(Vector2.One, "other") { Vec = new Vector2(70, 30), Dir = Direction2.Right } @@ -110,7 +110,7 @@ namespace Sandbox { var copy = obj.DeepCopy(); Console.WriteLine(copy); - var intoCopy = new Test {OtherTest = new Test()}; + var intoCopy = new Test(Vector2.One, "test") {OtherTest = new Test(Vector2.One, "other")}; obj.DeepCopyInto(intoCopy); Console.WriteLine(intoCopy); @@ -234,6 +234,11 @@ namespace Sandbox { public Direction2 Dir { get; set; } public Test OtherTest; + [CopyConstructor] + public Test(Vector2 test, string test2) { + Console.WriteLine("Constructed with " + test + ", " + test2); + } + public override string ToString() { return $"{this.GetHashCode()}: {nameof(this.Vec)}: {this.Vec}, {nameof(this.Point)}: {this.Point}, {nameof(this.OtherTest)}: {this.OtherTest}, {nameof(this.Dir)}: {this.Dir}"; }