Why Is List When Passed Without Ref to a Function Acting Like Passed with Ref

Why is list when passed without ref to a function acting like passed with ref?

It does not act like its passed by ref.

void ChangeMe(List<int> list) {
list = new List<int>();
list.Add(10);
}
void ChangeMeReally(ref List<int> list) {
list = new List<int>();
list.Add(10);
}

Try it. Do you notice the difference?

You can only change the contents of list (or any reference type) if you pass it without a ref (because as others have said, you are passing a reference to the object on the heap and thus change the same "memory").

However you cannot change "list", "list" is a variable that points to an object of type List. You can only change "list" if you pass it by reference (to make it point somewhere else). You get a copy of the reference, which if changed, can only be observed inside your method.

Why are objects automatically passed by reference?

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.

Does C# pass a ListT to a method by reference or as a copy?

It's passed by reference. List<T> is a class, and all class instances are passed by reference.

My C# functions seems to be acting as if I am passing by ref, why does it do this?

This is correct, you are passing a reference to this array to the Function.
There is no copy of the array created.

If you want a copy to be created, you could call

ExampleFunction(Test.ToArray());

Using Collections and Ref Keyword

In C# there are two kinds of types: reference and value types (there is also a pointer type, but is only used in unsafe contexts)

Value types contain the value directly, while reference types contain a reference to the data.

Value Types

When passing a variable of value type as a parameter, the method receives a copy of the original object, so that any modification made inside the method will not persist.

Value types consist of all numerical types, bool and enumerations and structs you define yourself.

(The main types that are value are structs and enumerations, since the numerical values and the bool are implemented as struct).

Note that you can still pass a value type by reference by using the ref keyword.

Reference types

When passing a reference type as a parameter, the method receives a copy of the reference, so the method will work with the same object. So any modification made inside the method will persist.

In C#, string has a special behavior. It is passed by reference and is immutable (it cannot be modified - if you modify a string object, the new value is copied into another object and your old variable references your new value.)

Every user-defined class, interface and delegate is passed by reference.
Also, every array is passed by reference.

So, in your case, you pass a List<int> which is an instance of a class, so it is passed by reference.

Hope this clarifies things a bit.

Best of luck!

Update based on the new question

Just as @fafase said, you are doing two things there:

  • when you add elements to the list (in the method), you are modifying the original list.
  • when you return the same reference, so when you do:

elements = Method(elements);
you actually assign elements to the list you return from the method (which is the same you passed in the first place, since it is passed by reference)

In short, if you only need to modify the list, simply do it in a void method, like in the original question.

Why Dart is acting as pass by reference?

I think your confusion comes from a misunderstanding about what variables and parameters are and the concept of pass by reference and pass by value.

A variable in Dart can be seen as a reference to a object. When you give this variable as a parameter to a method you are essential given a copy of this reference. The method can therefore not change what object this reference are pointing to. But the method do have access to the object itself since it got a copy of the reference. We are therefore not creating copies of objects when we give a variable to a method.

Some objects in Dart are immutable (like String, int, double etc.) so a method will not be able to change the inner state of these types of objects. So even if we got a copy of a reference to some of these objects, we cannot change the state and therefore we seen this like "pass by value" behavior.

But if an object (e.g. List) are able to change its inner state, we can make modification like adding items to a List object. Since we never create new objects we will see this change outside our method since we have made a change to the object itself without any change to the reference. This gives this "pass by reference" behavior you see in your example.

But this does not change the fact that the variable itself are a reference which always "pass by value" when given to methods as parameter.

A small example showing what I mean by "pass by value" when we talk about parameter to methods, can be seen here:

class A {
int value;

A(this.value);
}

void main() {
final a = A(1);
print(a.value); // 1
myMethod(a);
print(a.value); // 1
}

void myMethod(A a) {
a = A(2);
}

As the example shows, the a variable in myMethod can be changed to point to another object but since this variable is pass by value, the change will not happen in main.

Passing Objects By Reference or Value in C#

Objects aren't passed at all. By default, the argument is evaluated and its value is passed, by value, as the initial value of the parameter of the method you're calling. Now the important point is that the value is a reference for reference types - a way of getting to an object (or null). Changes to that object will be visible from the caller. However, changing the value of the parameter to refer to a different object will not be visible when you're using pass by value, which is the default for all types.

If you want to use pass-by-reference, you must use out or ref, whether the parameter type is a value type or a reference type. In that case, effectively the variable itself is passed by reference, so the parameter uses the same storage location as the argument - and changes to the parameter itself are seen by the caller.

So:

public void Foo(Image image)
{
// This change won't be seen by the caller: it's changing the value
// of the parameter.
image = Image.FromStream(...);
}

public void Foo(ref Image image)
{
// This change *will* be seen by the caller: it's changing the value
// of the parameter, but we're using pass by reference
image = Image.FromStream(...);
}

public void Foo(Image image)
{
// This change *will* be seen by the caller: it's changing the data
// within the object that the parameter value refers to.
image.RotateFlip(...);
}

I have an article which goes into a lot more detail in this. Basically, "pass by reference" doesn't mean what you think it means.



Related Topics



Leave a reply



Submit