5

I want to set an object equal to other object values in C# core, not by reference!

x.len=10;
val y=x;
y.len=x.len*2;

I expect y.len to be set to 20 and x.len to remain unchanged, but both y.len and x.len are 20, I am looking for a way to disconnect x and y.

I remember that in VB6 we had something like ByRef and ByVal which gave us the ability to control this thing, is there a similar thing in C# Core 3.x?

Kate Orlova
  • 3,225
  • 5
  • 11
  • 35
Mertez
  • 1,061
  • 3
  • 14
  • 38
  • 2
    It sounds like you want a value type rather than a reference type. You haven't shown any types here, but if `x` and `y` are references to the same object, you *will* get the effect you want. Note that this is quite separate from pass-by-reference and pass-by-value which is about how *method arguments/parameters* are handled. – Jon Skeet Mar 19 '20 at 12:16
  • minor nit: there's no such thing as "C# Core 3.x" - there's .NET Core 3.x (soon to be replaced by .NET 5.x), and there's "C# x.y" (currently at C# 8.*), but no "C# Core"; but yes, C# and .NET have a range of by-val and by-ref semantics including regular vs ref-locals, pass-by-value vs pass-by-ref parameters, and value-types vs reference-types, but it is impossible to know what applies without more of an example of what you're actually doing in this specific example; most importantly: "what is x?" – Marc Gravell Mar 19 '20 at 12:33
  • I found the best solution [here](https://stackoverflow.com/a/11308879/913608) as an extension method – Mertez Mar 19 '20 at 13:19
  • Solution you have found is using reflection an it fails for variety of complex objects, check the git repository issues of the code, that is pasted in the link – Mrinal Kamboj Mar 19 '20 at 13:49

2 Answers2

2

I want to set an object equal to other object values in c# core, not by reference!

For that either you need a value type like struct, which is not allocated on a heap or for reference type you need a deep copy not a shallow copy. In .Net C# all classes are by default allocated on heap, are reference type and when object is passed, then its either reference by value (which is another pointer to same memory) or reference by reference (pointer to pointer, which is using ref and out keywords)

Let's review the code

var y=x;, looking at the result, it is safe to assume that both y and x are of same type but both are reference types. Therefore if value type is not an option for you, then do one of the following:

Create a deep copy by:

  1. Serialization, preferably binary as its compact, for non circular object hierarchy serialization works well, when you deserialize then its a new object
  2. Use copy constructor, where an object is copied property by property, field by field into a fresh allocation

Once you do it, you will find un-like shallow copy one object will not modify the other, when the property is modified

Mrinal Kamboj
  • 11,300
  • 5
  • 40
  • 74
  • Thank @Mrinal, my object has many properties, I cannot duplicate it manually – Mertez Mar 19 '20 at 12:57
  • Then try serialization is the best option, there are many good binary serializers for .Net framework like proto-buf, implemented by Marc who has also answered the question. Ideally even for copy its best to implement `IClone`, which expose the DeepClone for every object and its done as part of the class definition not separately – Mrinal Kamboj Mar 19 '20 at 13:00
  • 1
    clarification: protobuf is a Google invention/property; I only claim ownership over protobuf-net, an independent implementation of protobuf – Marc Gravell Mar 19 '20 at 13:11
1

The simplest way to "disconnect" things is for them to be value types in the first place; if x is some kind of struct, then what you have will work already, however: mutable structs is usually a terrible idea and causes all kinds of confusion, so in reality I'd go with a different API there:

var x = new SomeReadonlyValueType(10);
var y = x; // this is a value copy
y = y.WithLength(x.len * 2); // mutation method

If you don't want to go that way, and you want x to be an instance of a mutable class, you'd need to deep-clone the instance instead, i.e.

x.len=10;
val y=x.DeepClone();
y.len=x.len*2;

Here, the point where they become separate is very clear and explicit; you'd need to implement DeepClone(), though - there is no standard inbuilt way of providing that. For simple cases, hand-rolled is fine; for complex cases, serialization is a popular way of doing it.


Example code for SomeReadonlyValueType:

readonly struct SomeReadonlyValueType
{
    public int Length { get; }
    public int Width { get; }
    public SomeReadonlyValueType(int length, int width)
    {
        Length = length;
        Width = width;
    }
    public override string ToString() => $"{Length} x {Width}";
    public SomeReadonlyValueType WithLength(int length)
        => new SomeReadonlyValueType(length, Width);
    public SomeReadonlyValueType WithWidth(int width)
        => new SomeReadonlyValueType(Length, width);
}
Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900