diff --git a/MLEM.Data/CopyExtensions.cs b/MLEM.Data/CopyExtensions.cs
index dbe03b9..4341509 100644
--- a/MLEM.Data/CopyExtensions.cs
+++ b/MLEM.Data/CopyExtensions.cs
@@ -16,13 +16,27 @@ namespace MLEM.Data {
/// The type of the object to copy
/// A shallow copy of the object
public static T Copy(this T obj, BindingFlags flags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) {
- var copy = (T) typeof(T).GetConstructor(Type.EmptyTypes).Invoke(null);
+ var copy = (T) Construct(typeof(T), flags);
obj.CopyInto(copy, flags);
return copy;
}
///
- /// Copies the given object into the given object .
+ /// Creates a deep copy of the object and returns it.
+ /// Note that, for this to work correctly, needs to contain a parameterless constructor.
+ ///
+ /// The object to create a deep copy of
+ /// The binding flags for field searching
+ /// The type of the object to copy
+ /// A deep copy of the object
+ public static T DeepCopy(this T obj, BindingFlags flags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) {
+ var copy = (T) Construct(typeof(T), flags);
+ obj.DeepCopyInto(copy, flags);
+ return copy;
+ }
+
+ ///
+ /// Copies the given object into the given object in a shallow manner.
///
/// The object to create a shallow copy of
/// The object to copy into
@@ -33,5 +47,38 @@ namespace MLEM.Data {
field.SetValue(otherObj, field.GetValue(obj));
}
+ ///
+ /// 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.
+ ///
+ /// The object to create a deep copy of
+ /// The object to copy into
+ /// The binding flags for field searching
+ /// The type of the object to copy
+ public static void DeepCopyInto(this T obj, T otherObj, BindingFlags flags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) {
+ foreach (var field in obj.GetType().GetFields(flags)) {
+ var val = field.GetValue(obj);
+ if (field.FieldType.IsValueType) {
+ // if we're a value type (struct or primitive), we can just set the value
+ field.SetValue(otherObj, val);
+ } else if (val != null) {
+ var otherVal = field.GetValue(otherObj);
+ // if the object we want to copy into doesn't have a value yet, we create one
+ if (otherVal == null) {
+ otherVal = Construct(field.FieldType, flags);
+ field.SetValue(otherObj, otherVal);
+ }
+ val.DeepCopyInto(otherVal, flags);
+ } else {
+ // if the value is null, we ensure that the value of the resulting object is also reset
+ field.SetValue(otherObj, null);
+ }
+ }
+ }
+
+ private static object Construct(Type t, BindingFlags flags) {
+ return t.GetConstructor(flags, null, Type.EmptyTypes, null).Invoke(null);
+ }
+
}
}
\ No newline at end of file
diff --git a/Sandbox/GameImpl.cs b/Sandbox/GameImpl.cs
index 9012b2f..6c0ee56 100644
--- a/Sandbox/GameImpl.cs
+++ b/Sandbox/GameImpl.cs
@@ -92,17 +92,19 @@ namespace Sandbox {
var obj = new Test {
Vec = new Vector2(10, 20),
Point = new Point(20, 30),
- Rectangle = new Rectangle(1, 2, 3, 4),
- RectangleF = new RectangleF(4, 5, 6, 7).ToMlem(),
- Dir = Direction2.Left
+ Dir = Direction2.Left,
+ OtherTest = new Test {
+ Vec = new Vector2(70, 30),
+ Dir = Direction2.Right
+ }
};
Console.WriteLine(obj);
- var copy = obj.Copy();
+ var copy = obj.DeepCopy();
Console.WriteLine(copy);
- var intoCopy = new Test();
- obj.CopyInto(intoCopy);
+ var intoCopy = new Test {OtherTest = new Test()};
+ obj.DeepCopyInto(intoCopy);
Console.WriteLine(intoCopy);
var writer = new StringWriter();
@@ -214,12 +216,11 @@ namespace Sandbox {
public Vector2 Vec;
public Point Point;
- public Rectangle Rectangle;
- public MLEM.Misc.RectangleF RectangleF;
public Direction2 Dir { get; set; }
+ public Test OtherTest;
public override string ToString() {
- return $"{nameof(this.Vec)}: {this.Vec}, {nameof(this.Point)}: {this.Point}, {nameof(this.Rectangle)}: {this.Rectangle}, {nameof(this.RectangleF)}: {this.RectangleF}, {nameof(this.Dir)}: {this.Dir}";
+ return $"{this.GetHashCode()}: {nameof(this.Vec)}: {this.Vec}, {nameof(this.Point)}: {this.Point}, {nameof(this.OtherTest)}: {this.OtherTest}, {nameof(this.Dir)}: {this.Dir}";
}
}