C
C#2y ago
Waffen

❔ Deep clone variable

How to create a complete, isolated copy of a variable to prevent the copy from changing when changed parent variable. I have tried some variants: 1. The most easiest variant
var parent = new Class();
var clonedParent = parent;
// do some changes with parent
if (parent == clonedParent) <-- True
var parent = new Class();
var clonedParent = parent;
// do some changes with parent
if (parent == clonedParent) <-- True
2. Variant with ICloneable *logic still not changed
class Person : ICloneable
{
public string Name { get; set; }
public int Age { get; set; }

public object Clone()
{
return new Person { Name = this.Name, Age = this.Age };
}
}
class Person : ICloneable
{
public string Name { get; set; }
public int Age { get; set; }

public object Clone()
{
return new Person { Name = this.Name, Age = this.Age };
}
}
if (parent == clonedParent) <-- True Still True 3. A little bit harder variant with ICloneable
class myClass : ICloneable
{
public string Name { get; set; }
public int Age { get; set; }

public object Clone()
{
return this.MemberwiseClone();
}
}
class myClass : ICloneable
{
public string Name { get; set; }
public int Age { get; set; }

public object Clone()
{
return this.MemberwiseClone();
}
}
if (parent == clonedParent) <-- True Still True Do you know any other variants to clone and isolate harold
67 Replies
Jimmacle
Jimmacle2y ago
you could use structs which are copied on assignment, or use records that you can copy using with {}
Waffen
WaffenOP2y ago
or use records that you can copy using with {} Do you mean this variant?
public object Clone()
{
return new Person { Name = this.Name, Age = this.Age };
}
public object Clone()
{
return new Person { Name = this.Name, Age = this.Age };
}
Jimmacle
Jimmacle2y ago
no, i mean an actual record
ero
ero2y ago
the Clone() version definitely doesn't return true
MODiX
MODiX2y ago
Ero#1111
REPL Result: Success
Person p = new();
Person pCloned = p.Clone() as Person;

Console.WriteLine(p == pCloned);

class Person : ICloneable
{
public string Name { get; set; }
public int Age { get; set; }

public object Clone()
{
return new Person { Name = this.Name, Age = this.Age };
}
}
Person p = new();
Person pCloned = p.Clone() as Person;

Console.WriteLine(p == pCloned);

class Person : ICloneable
{
public string Name { get; set; }
public int Age { get; set; }

public object Clone()
{
return new Person { Name = this.Name, Age = this.Age };
}
}
Console Output
False
False
Compile: 747.068ms | Execution: 94.422ms | React with ❌ to remove this embed.
Waffen
WaffenOP2y ago
It is just example My code is too big, i can't send it here
Jimmacle
Jimmacle2y ago
public record SomeRecord(string Name, int Age);

var r1 = new SomeRecord("", 0);
var r2 = r1 with {};
public record SomeRecord(string Name, int Age);

var r1 = new SomeRecord("", 0);
var r2 = r1 with {};
ero
ero2y ago
well then it's a bad example, no? your "example" works perfectly fine you're newing up the Person. that cannot be equal to the old reference
Waffen
WaffenOP2y ago
Let me try
Jimmacle
Jimmacle2y ago
the first "variant" doesn't actually do what's being asked, it's not making a copy
Waffen
WaffenOP2y ago
¯\_(ツ)_/¯
ero
ero2y ago
but this is also flawed because in this instance, r1 == r2 will still be true just beause of a different reason
Jimmacle
Jimmacle2y ago
i interpreted his goal as wanting to make a copy, not just test equality
ero
ero2y ago
the way it's being tested leads me to believe there's more to this
Jimmacle
Jimmacle2y ago
true, then you'll get a deep copy too
Waffen
WaffenOP2y ago
Hm, thanks, let me try record before. But serialize then deserialize method sounds very good.
Jimmacle
Jimmacle2y ago
what are you actually trying to accomplish?
Waffen
WaffenOP2y ago
I need to make a copy of the UserProfile then make some changes to it then I need to compare oldProfile and newProfile and get this difference Ok, it doesn't work. It made copy, but it still changes two variables
ero
ero2y ago
but you made the changes to it, surely therefore you know what differences there are?
Jimmacle
Jimmacle2y ago
idk what you mean by "it changes two variables" it creates a new record with the same values as the first one, it doesn't change anything
Waffen
WaffenOP2y ago
Due to some I can't get these changes directly
var r1 = new SomeRecord("", 0);
var r2 = r1 with {};
// do some changes with r2
if (r1 == r2) <-- True
var r1 = new SomeRecord("", 0);
var r2 = r1 with {};
// do some changes with r2
if (r1 == r2) <-- True
Jimmacle
Jimmacle2y ago
that isn't copying anything, classes are by reference
Waffen
WaffenOP2y ago
let me try this method firstly
ero
ero2y ago
what, no try anything else first
Jimmacle
Jimmacle2y ago
this does not mean they are the same object if you want to compare that, use .ReferenceEquals not ==
Waffen
WaffenOP2y ago
changed example for you:
var r1 = new SomeRecord("", 0);
var r2 = r1 with {};
// do some changes with r2
if (r1 == r2) <-- True
var r1 = new SomeRecord("", 0);
var r2 = r1 with {};
// do some changes with r2
if (r1 == r2) <-- True
ero
ero2y ago
this means they have the identical contents
MODiX
MODiX2y ago
Ero#1111
REPL Result: Failure
R r1 = new("Foo", 123);
R r2 = r1 with {};

return (r1, r2, r1 == r2, r1.ReferenceEquals(r2));

record R(
string Name,
int Age);
R r1 = new("Foo", 123);
R r2 = r1 with {};

return (r1, r2, r1 == r2, r1.ReferenceEquals(r2));

record R(
string Name,
int Age);
Exception: CompilationErrorException
- There is no argument given that corresponds to the required parameter 'objB' of 'object.ReferenceEquals(object?, object?)'
- There is no argument given that corresponds to the required parameter 'objB' of 'object.ReferenceEquals(object?, object?)'
Compile: 612.249ms | Execution: 0.000ms | React with ❌ to remove this embed.
Jimmacle
Jimmacle2y ago
even so, how does that indicate it "changed two variables"
ero
ero2y ago
ReallyMad
MODiX
MODiX2y ago
Ero#1111
REPL Result: Success
R r1 = new("Foo", 123);
R r2 = r1 with {};

return (r1, r2, r1 == r2, object.ReferenceEquals(r1, r2));

record R(
string Name,
int Age);
R r1 = new("Foo", 123);
R r2 = r1 with {};

return (r1, r2, r1 == r2, object.ReferenceEquals(r1, r2));

record R(
string Name,
int Age);
Result: ValueTuple<R, R, bool, bool>
{
"item1": {
"name": "Foo",
"age": 123
},
"item2": {
"name": "Foo",
"age": 123
},
"item3": true,
"item4": false
}
{
"item1": {
"name": "Foo",
"age": 123
},
"item2": {
"name": "Foo",
"age": 123
},
"item3": true,
"item4": false
}
Compile: 572.211ms | Execution: 117.820ms | React with ❌ to remove this embed.
Jimmacle
Jimmacle2y ago
it's just saying the two objects are equal according to the == operator which can be overridden (and is overridden by records to compare them by value) instead of testing something else and making assumptions about other behavior, test the actual behavior you're trying to achieve
cap5lut
cap5lut2y ago
the with thing will generally only do shallow copies, so probably not whats wanted either
Jimmacle
Jimmacle2y ago
based on the model posted earlier a shallow copy is sufficient
ero
ero2y ago
ask insufficient questions, get insufficient answers ¯\_(ツ)_/¯
Waffen
WaffenOP2y ago
I needed just a method how to do deep copy
ero
ero2y ago
there's no one correct answer
Waffen
WaffenOP2y ago
serialize then deserialize the best method for me i guess.
ero
ero2y ago
i guess
cap5lut
cap5lut2y ago
performance wise, its the worst u can do
Waffen
WaffenOP2y ago
Yes, but is there any other variants?
ero
ero2y ago
do it all recursively i guess
Waffen
WaffenOP2y ago
I'm not sure if this method is right for my problem
Jimmacle
Jimmacle2y ago
well, you want a deep copy
cap5lut
cap5lut2y ago
the easiest and most efficient way is to use the 2nd variant, after all u have to take care of references inside ur instances
Waffen
WaffenOP2y ago
What "2nd variant" are you talking about?
cap5lut
cap5lut2y ago
the ICloneable implementation and actually manually creating the copy inside the Clone method
Waffen
WaffenOP2y ago
I've tried this variant already, it stil True
ero
ero2y ago
i quite literally proved that that is not possible
Jimmacle
Jimmacle2y ago
that's not possible
Waffen
WaffenOP2y ago
Okey
cap5lut
cap5lut2y ago
did u make copies of nested of mutable references as well?
Jimmacle
Jimmacle2y ago
== tests reference equality for classes unless overridden you're returning a new object, so it cannot be the same reference
cap5lut
cap5lut2y ago
well, unless u show exactly what u tried we cant tell what u did wrong, after all u implemented it urself
public class Box : IClonable {
public List<object> Items { get; set; } = new();
public object Clone() {
return new Box { Items = Items }; // this would not be enough
}
}

var box1 = new Box();
var box2 = box1 as Box;
public class Box : IClonable {
public List<object> Items { get; set; } = new();
public object Clone() {
return new Box { Items = Items }; // this would not be enough
}
}

var box1 = new Box();
var box2 = box1 as Box;
here u have only a shallow copy, because box1.Items and box2.Items would reference the same List<object> instance. u would have to make a copy of the list as well. and then u need to think about if need to make a copy of each element in the list as well, and then again if take care about their references
Waffen
WaffenOP2y ago
Thanks a lot! I did not know how to correctly formulate my problem, what to correctly google the solution. Deep clone through Serialize and deserialize helps me and takes 0.0758792 seconds. For my project, this delay is not a problem.
Jimmacle
Jimmacle2y ago
i'm not sure how you benchmarked it but i'm very skeptical it took 70ms to serialize and deserialize a 2 field class you should use something like https://benchmarkdotnet.org/
cap5lut
cap5lut2y ago
different hardware would give different results, also the (de-)serialization methods
Jimmacle
Jimmacle2y ago
not 70ms different that sounds way too high
cap5lut
cap5lut2y ago
wouldnt alone reflection based and source generation based make quite a huge difference? also the difference is currently 6ms and we dont know the actual structure of the data they tested with
ero
ero2y ago
It's just user data, it can't be that much Unless the project or game is already hugely complex
Ryada
Ryada2y ago
Isn't it easier at all to just get the difference before adjusting an object? Or just newing up an object with replacements in the way of
var secondProfile = new Profile()
{
Name = oldProfile.Name,
Rank = oldProfile.Rank,
Experience = newExperience ?? oldProfile.Experience,
};
var secondProfile = new Profile()
{
Name = oldProfile.Name,
Rank = oldProfile.Rank,
Experience = newExperience ?? oldProfile.Experience,
};
ero
ero2y ago
that's essentially what the record suggestion was
Profile p1 = new();
Profile p2 = p1 with
{
Experience: newExperience ?? oldProfile.Experience
};
Profile p1 = new();
Profile p2 = p1 with
{
Experience: newExperience ?? oldProfile.Experience
};
Ryada
Ryada2y ago
Ah I missed that one then my bad was a lot of messages
DaVinki
DaVinki2y ago
What’s wrong with the 2nd option? Why did they resort to serializing and deserializing?
ero
ero2y ago
nobody knows
cap5lut
cap5lut2y ago
more to write i guess 😂
Accord
Accord2y ago
Was this issue resolved? If so, run /close - otherwise I will mark this as stale and this post will be archived until there is new activity.
Want results from more Discord servers?
Add your server