Nefzen
Nefzen

Reputation: 7929

using ref with class C#

I want to give a certain linked list to a class I am making. I want the class to write into that list (eg by .addLast()).

Should I use the ref keyword for that?

I am somewhat puzzled on where to use the ref and out keywords in C#, as all classes are allocated dynamically on the heap and we actually use pointers for most operations.
Of course, out and ref keywords make sense for primitives and structs.

Also, if I don't send the list directly, but send a class containing the list? (it's internal and needed), do I still need to use ref? or if I pass it between functions, ex:

void A(ref LinkedList<int> list){
    B(list);
}

void B(ref LinkedList<int> list){
    _myList = list;
}

Upvotes: 22

Views: 34288

Answers (7)

Noldorin
Noldorin

Reputation: 147260

This is a common misconception of the use of ref keyword in C#. Its purpose is to pass either a value or a reference type by reference, and you only need it in specific circumstances where you need a direct reference to the actual argument, rather than a copy of the argument (be it a value or reference itself). It is imperative not to confuse reference types with passing by reference in any case.

Jon Skeet has written an excellent article about parameter passing in C#, which compares and contrasts value types, reference types, passing by value, passing by reference (ref), and output parameters (out). I recommend you take some time to read through this in full and your understanding should become much clearer.

To quote the most important parts from that page:

Value parameters:

By default, parameters are value parameters. This means that a new storage location is created for the variable in the function member declaration, and it starts off with the value that you specify in the function member invocation. If you change that value, that doesn't alter any variables involved in the invocation

Reference parameters:

Reference parameters don't pass the values of the variables used in the function member invocation - they use the variables themselves. Rather than creating a new storage location for the variable in the function member declaration, the same storage location is used, so the value of the variable in the function member and the value of the reference parameter will always be the same. Reference parameters need the ref modifier as part of both the declaration and the invocation - that means it's always clear when you're passing something by reference. Let's look at our previous examples, just changing the parameter to be a reference parameter:

To conclude: having read my reply and Jon Skeet's article, I hope that you will then see that there is no need whatsoever for using the ref keyword in the context of your question.

Upvotes: 33

bueller
bueller

Reputation: 317

I know this is an old question, but none of the answers give a good direct reason why in my opinion.

You don't need to use ref in this instance, and here's why. Consider this function:

void Foo(MyClass a1, ref MyClass a2, out MyClass b1, int c1, MyStruct d1, ref MyStruct d2)
{
}

Now call this function as

MyClass  a = new MyClass();
MyClass  b = null
int      c = 3;
MyStruct d = new MyStruct();

Foo(a, ref a, b, c, d, ref d);

Here's what you get inside of the function:

void Foo(MyClass a1, ref MyClass a2, 
         out MyClass b1, 
         int c1, 
         MyStruct d1, ref MyStruct d2)
{
   a1 is a copy in memory of the pointer to the instantiated class a;
   a2 is the pointer to the instantiated class a;

   b1 is the pointer to b, but has the additional check of having to be set within this function - and cannot be used before being set;

   c1 is a copy in memory of the variable c;

   d1 is a copy in memory of the struct d;
   d2 is the struct d;
}

Important things to realize:

  1. setting a1 to null will not set a to null.
  2. setting a2 to null will set a to null.
  3. setting b1 is required.
  4. setting c1 will not change c.
  5. setting d1 will not change d.
  6. setting d2 will change d.

This allows for some weirdness like this:

void Foo(MyClass x, ref MyClass y)
{
    x = null;
    y.Bar("hi");
}

Called like:

MyClass a = new MyClass();
Foo(a, ref a);

You are using a class, and so your situation is more like variable a1 in the function call. Which means ref isn't strictly required.

The Jon Skeet article won't help you that much because his example with IntHolder is a struct not a class. Struct is value type like int and must be handled the same way.

Upvotes: 3

dss539
dss539

Reputation: 6950

No you don't need to use ref.

LinkedList is an object, so it is already a reference type. The parameter list is a reference to the LinkedList object.

See this MSDN article for a description of value types. Value types are usually the parameters you would use the ref or out keywords with.

You may also want to pass reference types by ref. This will allow you to point the reference to another object.

Any time you pass an object o you are really passing a reference to the object. When you pass a `ref object o' you are passing a reference to the reference. This allows to you modify the reference.

Passing Reference-Type Parameters may also help you understand.

Upvotes: 1

Spencer Ruport
Spencer Ruport

Reputation: 35107

The only time you need to use ref with a reference type is if you're going to be creating a new object inside a function.

Example #1: ref keyword not necessary.

// ...
   List myList = new List();
   PopulateList(myList);
// ...
void PopulateList(List AList)
{
   AList.Add("Hello");
   AList.Add("World");
}

Example #2: ref keyword necessary.

// ...
   List myList;
   PopulateList(ref myList);
// ...
void PopulateList(ref List AList)
{
   AList = new List();
   AList.Add("Hello");
   AList.Add("World");
}

Upvotes: 19

Nefzen
Nefzen

Reputation: 7929

I am adding this answer for programmers that are used to C++ like myself.

Classes, interfaces, delegatess and arrays are reference types, meaning that they have an underlying pointer. Normal function calls copy this pointer(reference) by value, while sending by reference sends sends a reference to this reference:

//C# code:
void Foo(ClassA     input)
void Bar(ClassA ref input)

//equivalent C++ code:
void Foo(ClassA*  input)
void Bar(ClassA*& input)

Primitives such as int, double, etc structs and strings(string are an exception to these, but works similar), are allocated on the heap, so things work a bit different:

//C# code:
void Foo(StructA     input)
void Bar(StructA ref input)

//equivalent C++ code:
void Foo(StructA  input)
void Bar(StructA& input)

the ref keyword needs to be used both in declaration of the method and when calling it, so it would be clear it is referenced:

//C# code:
void Foobar(ClassB ref input)
...
ClassB instance = new ClassB();
Foobar(ref instance);

//equivalent C++ code:
void Foobar(ClassB*& input)
...
ClassB instance* = new ClassB();
Foobar(instance);

As said before, please read this detailed explanation. It also explains about strings.



It is interesting to note that calling by reference works with an underlying pointer, so we get to this code:

//C# code:
void Foo(ClassA input){
    input = input + 3;
}
void Bar(ClassA ref input){
    input = input + 3;
}
//equivalent C++ code:
void Foo(ClassA&  input){
    input = input + 3;
}
void Bar(ClassA*&  input){
    *input = *input + 3;
}
//equivalent pure C code:
void Fun(ClassA* input){
    *input = *input + 3;
}
void Fun(ClassA** input){
    *(*input) = *(*input) + 3;
}

it's a rough equivalent, but it's somewhat true.

Upvotes: 2

Timothy Carter
Timothy Carter

Reputation: 15785

In the two snippets you posted there is no need to pass list by ref. To quote Jon Skeet, object references are passed by value. This means, you would want to ref a reference type when the method will or might change the object reference and you want this new reference to carry back to the calling method. For example:

void methodA(string test)
{
    test = "Hello World";
}

void methodB(ref string test)
{
    test = "Hello World";
}

void Runner()
{
    string first= "string";
    methodA(first);
    string second= "string";
    methodB(ref second);
    Console.WriteLine((first == second).ToString()); //this would print false
}

Upvotes: 2

user27414
user27414

Reputation:

For what you're doing you don't need to use ref. If you did pass the list using ref, you would be allowing the caller to change which list you're referencing, rather than just changing the contents of the list.

Upvotes: 23

Related Questions