using ref with class C#

26,459

Solution 1

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.

Solution 2

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.

Solution 3

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

Solution 4

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.

Solution 5

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.

Share:
26,459
Nefzen
Author by

Nefzen

Updated on July 09, 2022

Comments

  • Nefzen
    Nefzen almost 2 years

    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;
    }
    
  • Noldorin
    Noldorin almost 15 years
    This is slightly misleading, as it implies that ref should only be used with non-references types (i.e. value types). It can in fact be used just as well with either, and commonly is!
  • Hans Van Slooten
    Hans Van Slooten almost 15 years
    This is actually more than slightly misleading. The "ref" keyword has nothing to do with whether it is a reference type. It is used for being able to change the reference that is passed in.
  • dss539
    dss539 almost 15 years
    Thanks for the notes I'll try to clarify that in the text.
  • Noldorin
    Noldorin almost 15 years
    @HVS: Well, it depends how you interpreted the statement. It was pretty vague, so there's a lot of scope. I was probably being kind with the "slightly". Indeed, the use of ref and reference types is completely independent, as I explain in my answer.
  • configurator
    configurator almost 15 years
    Indeed, this is extermely misleading. "No you don't need to use ref. It's already a reference type." implies that the ref keyword does nothing when used with reference types and should never be used. This is a bad answer in my mind.
  • dss539
    dss539 almost 15 years
    @configurator, that wasn't the intended meaning but I can see how you might interpret it that way.
  • Nefzen
    Nefzen almost 15 years
    oh, so the value copied is the reference and not something like .Clone(). This makes sense.
  • Nefzen
    Nefzen almost 15 years
    the answer is good, but however it gave me the opposite impression. I am reading Jon Skeet's article now, thanks.
  • Nefzen
    Nefzen almost 15 years
    I had the impression that sending a class by value will call the copy constructor, because it is copied "by value". That made even less sense with no need whatsoever to use ref, so I got to the right answer in the end.
  • Noldorin
    Noldorin almost 15 years
    Passing a class by value does copy the value. The key here is that the "value" is actually a reference to the object -so you're really just creating a new reference that points to the same object (reference type) on the heap.
  • Noldorin
    Noldorin almost 15 years
    And don't worry, I would begrudge you for accepting the other answer! ;)
  • Jason
    Jason over 11 years
    Example 2, Line 2: PopulateList(ref myList)
  • supercat
    supercat over 10 years
    I find it helpful to think of a class-type variable Foo which holds a reference to the 9,421st object created as holding "Object #9421". A statement Foo.Bar() thus means "Execute Bar on object #9421". A statement Boz.Bar(Foo) tells Bar that the object it's interested in is #9421, but doesn't let Bar change Foo--it will still hold "Object #9421". By contrast, Boz.Bar(ref Foo) would let Bar change Foo to hold "Object #24601" if it so chose.
  • MaYaN
    MaYaN about 7 years
    @SpencerRuport Your explanation made it rock solid for me. Thanks