C# Core 3.1 ByRef and ByVal

Viewed 567

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?

2 Answers

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

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);
}
Related