Hamish Grubijan
Hamish Grubijan

Reputation: 10820

Should the argument be passed by reference in this .net example?

I have used Java, C++, .Net. (in that order). When asked about by-value vs. by-ref on interviews, I have always done well on that question ... perhaps because nobody went in-depth on it. Now I know that I do not see the whole picture.

I was looking at this section of code written by someone else:

XmlDocument doc = new XmlDocument();
AppendX(doc); // Real name of the function is different
AppendY(doc); // ditto

When I saw this code, I thought: wait a minute, should not I use a ref in front of doc variable (and modify AppendX/Y accordingly? it works as written, but made me question whether I actually understand the ref keyword in C#.

As I thought about this more, I recalled early Java days (college intro language). A friend of mine looked at some code I have written and he had a mental block - he kept asking me which things are passed in by reference and when by value. My ignorant response was something like: Dude, there is only one kind of arg passing in Java and I forgot which one it is :). Chill, do not over-think and just code.

Java still does not have a ref does it? Yet, Java hackers seem to be productive. Anyhow, coding in C++ exposed me to this whole by reference business, and now I am confused.

Should ref be used in the example above?

I am guessing that when ref is applied to value types: primitives, enums, structures (is there anything else in this list?) it makes a big difference. And ... when applied to objects it does not because it is all by reference. If things were so simple, then why would not the compiler restrict the usage of ref keyword to a subset of types.

When it comes to objects, does ref serve as a comment sort of? Well, I do remember that there can be problems with null and ref is also useful for initializing multiple elements within a method (since you cannot return multiple things with the same easy as you would do in Python).

Thanks.

Upvotes: 2

Views: 2787

Answers (9)

Eric Lippert
Eric Lippert

Reputation: 659956

Lots of people are confused by this. Here's how I think of it. I don't think of "ref" as meaning "by reference" at all. I think of "ref" as meaning "alias". That is, when you say

void M(ref int x) { x = 123; }
void N(int z) { ... }
...
int y = 456;
M(ref y);
N(y);

what that "ref y" means is "please make the corresponding formal parameter x an alias to the variable y". That is, x and y now are both variables for the same storage location. When you write to x, you're writing to y because x is another name for y.

When you pass without ref, as in N(y) you're saying "y and z are two different variables such that z begins its lifetime with the same contents as y".

Once you start thinking about it like that you don't have to worry about pass-by-ref vs pass-by-value, blah blah blah, it's all very confusing. The key difference is that normal passing creates a new variable and initializes it with the argument, whereas ref makes an alias to an existing variable.

I wish we'd used "alias" instead of "ref"; it would have been much more clear.

Upvotes: 1

Jon Skeet
Jon Skeet

Reputation: 1499770

You're not alone in being confused.

By default, everything is passed by value in C# - but in the case of a reference type (such as XmlDocument) that value is a reference.

The ref keyword is used to indicate that a parameter is "pass by reference" and it has to be specifed at the call site as well. Java doesn't have any equivalent - everything in Java is passed by value.

See my article on parameter passing for a lot more detail.

Upvotes: 10

Hans Passant
Hans Passant

Reputation: 941218

If you've coded in C++ before then you must be familiar with pointers. An object reference in .NET code and Java is a pointer under the hood. It just isn't explicitly written in a syntax that makes it obvious that it is a pointer, you are supposed to memorize it. The rule isn't very hard, any variable that refers to an object of a class, an array, a string or System.Object is a reference type and the variable is a pointer. Anything else is a value type and the variable contains the actual value.

When you pass such a variable to a method, you are passing the pointer value. The method can then modify the pointed-to object as it sees fit. Passing the pointer by reference doesn't make any difference, it is still the same pointer, pointing to the same object.

This is entirely different when you pass a value of a value type. If you want the calling method to modify that value then you have to generate a pointer to that value. You do so by using the "ref" keyword in the method declaration.

The outlier case is where you want the calling method to return a new object. In other words, modify the pointer value. Then you have to use the ref keyword, that creates a pointer to a pointer. You'd typically avoid that by having the method return the object as the method return value.

Upvotes: 1

Rune FS
Rune FS

Reputation: 21742

You're not alone at being confused and coding in c++ and c# I can understand why (not ment as critisism but as a comment since that's the world I live in too) in c++ when you use & on an argument to pass a reference you are basically saying this is an alias I will use inside the method for the argument being passed to the method. Anything you do to that argument will have the same effect as if you had used the variable it self. so in code you can do: void Foo(MyClass& arg) { arg = new MyClass(1); }

int x = new MyClass(0);
Foo(x);

or

int x = new MyClass(0);
void Foo()
{
   x = new MyClass(1);
}

in either case x now equals MyClass(1) (and you have a leak cuz there's no way you can get to the original but that's not my point). I guess from you question you knew that already but it will serve a purpose anyway :)

If you pass a reference in the standard is that the reference is passed by value. It's no longer an alias everything you do to the object being references will effect the object but if you do anything to the variable referencing the object (e.g. assigning a new object) then that will only affect the copy of the reference. Let's have some more code

c#

MyClass x = MyClass(0);
void Foo(MyClass arg) //reference being passed by val
{
  arg = new MyClass(1);
}
Foo(x);

x still equals MyClass(0)

MyClass x = MyClass(0);
void Foo(ref MyClass arg) //passing by ref
{
  arg = new MyClass(1);
}
Foo(ref x);

x equals MyClass(1)

So the standard argument passing in C# differs from passing a reference in C++ but using the ref keyword (which is not encouraged) gives you close to the same mechanics as & in c++ & in C++ is usually encouraged due to optimization/lack of copying how ever since you only copy the reference in C# that's not a concern and ref should only be used when you really need an alias for the variable being passed to the method aka when you potentially have to assign a new object instance to the variable rather than using/changing the state of an object

Upvotes: 1

OwenP
OwenP

Reputation: 25378

I argued this with some coworkers and ultimately lost; here's how I learned to do things their way.

The ref and out keywords mean "I may/will replace the current reference with a new reference". After calling the method, the variable might refer to a completely different object. On the other hand, it's always known that for reference types, the properties of that type might change and if they were important you should cache them.

I don't fully agree with it, but it definitely makes sense to have something to signify "the reference will change".

Upvotes: 1

csauve
csauve

Reputation: 6263

That depends on what you want AppendX to do. If it is modifying the contents of the object, it does not need to be passed by ref. If you wish AppendX to be able to change which object the variable "doc" points to, you need to. "doc" is already a "reference type", which is equivelant to it being a pointer to an object in c++.

Upvotes: 2

Jeff Sternal
Jeff Sternal

Reputation: 48583

A method that accepts a reference type variable passed by ref may change the thing the variable is pointing to - not just modify it.

Upvotes: 4

Femaref
Femaref

Reputation: 61427

No, don't use the ref keyword. You want to use the content of the doc, not change doc itself. See this post for further explenation: ref keyword

Upvotes: 2

Jamie Ide
Jamie Ide

Reputation: 49251

Object types are always passed by reference (actually the reference is passed by value). Integral types may be passed by reference or value.

Upvotes: 4

Related Questions