10

I have a general question about deep- and shallow-copy in the context of the pass-by-reference- and pass-by-value-concept of C#:

In C# it is a requirement to explicitly create methods that accept pointers/references to be able to pass such to the method. However, at least objects passed as parameters to methods/constructors are behaving differently from the rest. It seems they are always passed by reference if no extra cloning is done as described here: http://zetcode.com/lang/csharp/oopii/.

Why are objects automatically passed by reference? Is there any particular benefit from forcing the cloning process for them instead of treating objects more like int, double, boolean, etc. in these cases?

Here is code example that illustrates what I mean:

using System;

public class Entry
{

    public class MyColor
    {
        public int r = 0;
        public int g = 0;
        public int b = 0;
        public double a = 1;

        public MyColor (int r, int g, int b, double a)
        {
            this.r = r;
            this.g = g;
            this.b = b;
            this.a = a;
        }
    }

    public class A
    {
        public int id;
        public MyColor color;
        public MyColor hiddenColor;

        public A (int id, MyColor color)
        {
            this.id = id;
            this.color = color;
        }
    }

    static void Main(string[] args)
    {
        int id = 0;
        MyColor col = new MyColor(1, 2, 3, 1.0);

        A a1 = new A(id, col);
        A a2 = new A(id, col);

        a1.hiddenColor = col;
        a2.hiddenColor = col;

        a1.id = -999;
        id = 1;
        col.a = 0;

        Console.WriteLine(a1.id);
        Console.WriteLine(a2.id);

        Console.WriteLine(a1.color.a);
        Console.WriteLine(a2.color.a);

        Console.WriteLine(a1.hiddenColor.a);
        Console.WriteLine(a2.hiddenColor.a);
    }
}

This results in:

-999
0
0
0
0

Instances of MyCol are always passed by reference while the other arguments are passed by value. I would have to implement ICloneable in classes MyColor and A. On the other hand, the ´ref´-statement is present in C# which should be used to explicitly allow and do pass-by-reference.

Suggestions al welcomed!

Florian R. Klein
  • 1,375
  • 2
  • 15
  • 32
  • Not all instances are automatically pass-by-reference. Struct instances are pass-by-value - and it looks like your class should be a struct instead since it's a set of color values. – BoltClock Apr 13 '14 at 10:00
  • 5
    @BoltClock, actually, all types are passed by value (including reference types), unless specified otherwise. For reference types, it just means that the value that is passed is a reference, but it's still passed by value: assigning a new reference to the parameter won't affect the caller. – Thomas Levesque Apr 13 '14 at 10:13
  • @Thomas Levesque: That's absolutely true. I suppose I was being vague in my comment because I've heard there are people out there who think new users would find that distinction too confusing (resulting in, funny enough, arguments between experienced people on whether the distinction should be made) :( – BoltClock Apr 13 '14 at 10:17
  • 1
    @BoltClock, I agree that it *is* confusing at first, but the distinction is very important, so I think it's better to explain it accurately ;) – Thomas Levesque Apr 13 '14 at 10:18
  • @Thomas Levesque: Yeah, I agree :) – BoltClock Apr 13 '14 at 10:19
  • 1
    @ThomasLevesque I agree that the distinction is important, but the pedanticism about it is a bit silly. The object is not passed by value. Instead, what is passed by value is a reference to the object. It makes no sense to say that "an object is passed by value, but the value is something other than the object". And as long as what gets passed to the function is **a reference** to the object, it is silly to claim that "objects are not passed by reference". – jalf Apr 13 '14 at 10:30
  • 1
    To truly understand what is going on, you need to understand *pointers*. An object reference is nothing but a pointer. Passing a value by reference passes a pointer to the value. The C language is a good language to learn, it forces you to think about pointers and does very little to abstract the machine away. Which is why it is a fast language. And a dangerous one. – Hans Passant Apr 13 '14 at 10:32
  • @HansPassant, true, but the fact that references are actually pointers is just an implementation detail. It could be just an identifier that is mapped in an object table, and it would make no difference. – Thomas Levesque Apr 13 '14 at 10:36
  • Hmya, SO users keep asking about implementation details. A simple "it is just a pointer" answers the question fully and gives instant insight. As long as they know what a pointer is anyway, the usual hangup. – Hans Passant Apr 13 '14 at 10:43
  • @HansPassant While I know what you mean, the word "pointer" here might confuse the terminology even more since in C# a *pointer* is related to value types. For example `double*` and `TimeSpan*` are types of pointers. Those pointers can actually be incremented, or an integer can be added to them, and similar "arithmetic". The references related to `class` types don't have that. They are just "arrows" with no "addresses" (in C#, actual implementation might use addresses, of course, but that is invisible to the user). – Jeppe Stig Nielsen Apr 13 '14 at 11:04
  • It was more like I needed to understand, how the reference-value is actually used in C#. For all Object-types we seem to juggle reference-values which, used as pointers, point you to the right memory address. I just wonder why they implemented it like this. Knowing, that objects are not single values in memory but more complex comprehensive construct, it is of course a good idea but with some traps in it, too. – Florian R. Klein Apr 13 '14 at 11:07
  • People, who downvote against the common opinion should consider putting a comment/statement why they think this way... – Florian R. Klein Apr 28 '14 at 16:25

4 Answers4

45

Why are objects automatically passed by reference?

They're not.

Is there any particular benefit from forcing the cloning process for them instead of treating objects more like int, double, boolean, etc. in these cases?

There's no "cloning process" for reference types, only for value types.

I think you're confusing different concepts:

  • value types vs. reference types

    For value types (such as primitive numeric types, enums, and structures like DateTime), the value of the variable is the object itself. Assigning the variable to another (or passing it as a parameter by value) creates a copy of the object.

    For reference types (such as object, string, classes (not structs) etc), the value of the variable is a reference to the object. Assigning the variable to another (or passing it as a parameter by value) creates a copy of the reference, so it still refers to the same object instance.

  • passing parameters by value vs. by reference

    Passing parameters by value means that you pass a copy of the value. Depending on whether it's a value type or reference types, that means a copy of the object itself, or a copy of the reference. If the callee modifies members of a value type passed as a parameter, the caller won't see the changes, since the callee is working on a copy. On the other hand, if the callee modifies members of a reference type passed as a parameter, the caller will see the changes, because the callee and caller both have a reference to the same object instance.

    Passing parameters by reference means that you pass a reference to a variable (which may be a variable of value type or reference type). The value is not copied: it is shared between the caller and the callee. So any change made by the callee (including assignment of a new value to the parameter) will be seen by the caller.

    Unless specified otherwise (with the ref or out keywords), all parameters are passed by value, including reference types. It's just that for reference types, the value that is passed is a reference, but it's still passed by value.

I suggest you read Jon Skeet's article Parameter passing in C# for a better explanation.

this
  • 1,406
  • 11
  • 23
Thomas Levesque
  • 286,951
  • 70
  • 623
  • 758
  • 2
    Well explained. This question has been asked and answered so many times but people still confuse the concepts. I'd wish there were some other terms, because "reference type" is confused with "passing by reference". – Jeppe Stig Nielsen Apr 13 '14 at 10:35
  • So, the actual confusing part for me was the different interpretation of the passed values (actual value or reference) depending on the parameter-/variable-type. If I further use `ref` or `out` where references to Objects are passed, would this be totally obsolete? I tried to pass-by-reference (`ref`) the reference-value `MyColor`-instance to one of the `A`-instances and the results are just the same as passing the reference-value. – Florian R. Klein Apr 13 '14 at 11:00
  • @FlorianR.Klein, as long as the callee doesn't assign anything to the parameter, it makes no difference for reference types. But if it assigns a different object, then the caller's variable will also point to that object. That's the only case where passing reference types by reference is useful. – Thomas Levesque Apr 13 '14 at 11:06
  • Can't emphasize the Skeet article enough, especially this line "object references are passed by value by default", once you grok that it's helpful to see the origins of this behavior in the Self programming language: https://en.wikipedia.org/wiki/Self_(programming_language) – missaghi Aug 10 '18 at 03:30
3

All method arguments are passed by value unless you explicitly specify that they should be passed by reference using the ref or out keyword. That means that if you pass a variable to a method parameter then the contents of the variable is copied and passed to the method.

If the variable is a value type, which basically means a struct, then the variable contains an object and so that object is copied. If the variable is a reference type, which basically means a class then the variable contains a reference to an object so that reference is copied.

If you declare a parameter as ref or out then a reference to the variable is created and that is passed to the method. If the variable contains an object then a reference to that object is created and if the variable contains a reference then a reference to that reference is created.

jmcilhinney
  • 50,448
  • 5
  • 26
  • 46
  • Please can you advise what happens when an object of reference type if passed into a method using the ref keyword? – variable Nov 23 '18 at 10:36
  • I mean what I wrote and I wrote what I mean. If you have read what I wrote then you know what I mean. If it made no difference then the `ref` keyword wouldn't exist. – jmcilhinney Nov 23 '18 at 10:56
2

I'll rephrase your question: Why do we need classes? Can't we just have only structs?

Not all objects are safe to copy. You can't logically copy a FileStream or a Button for example. These objects have identity and you want all code to refer to the one and only object.

usr
  • 168,620
  • 35
  • 240
  • 369
0

A variable, parameter, or field of a class or interface type (collectively, "reference types") does not hold a class object; it holds an object identifier. Likewise, array of a reference type doesn't hold objects; it holds object identifiers.

Even though objects in .NET do not have any human-readable identifier associated with them, it may be helpful to reason about them as though they do: if at least some number (e.g. 592) of objects are created during the course of a program's execution, exactly one object will be the 592nd one created; once the 592nd object is created, no other object will ever be the 592nd. There's no way to find out which object is the 592nd, but if a variable which holds a reference to the 592nd object is passed as a non-ref parameter to some method, it will continue to hold a reference to the 592nd object when the method returns. If object #592 is a reference to an instance of Car which is colored red, a local variable myCar holds "Object ID #592", and one calls a method PaintCar(myCar);, then that method will receive Object #592". If that method paints the car blue, then when it returns, myCar will hold "Object #592", which will identify a blue car.

supercat
  • 77,689
  • 9
  • 166
  • 211