10

I have mainly worked in C++ and I am now using C# at my new job, and after doing some reading on here about the 'ref' keyword and c# value vs reference types I am still finding some confusion with them.

As far as I'm understanding these if you were passing these to a method these would be analogous C++ styles:

Value types:

public void CSharpFunc(value)

and

public void CPlusplusFunc(value)

Reference types:

public void CSharpFunc(reference)

and

public void CPlusPlusFunc(&reference)

'ref' / pointer

public void CSharpFunc(ref bar)

and

public void CPlusPlus(*bar)

Is this a correct analogy?

Gigi
  • 4,953
  • 24
  • 25
ElmsPlusPlus
  • 185
  • 7
  • The ref/pointer analogy is not a good one IMO. There's no pointer concept at all in C# (I mean, managed C# of course). – ken2k Aug 13 '12 at 13:33
  • @ken2k OK, but in C# `ref int a` gives you the same ability as `int *a` in c/c++. It lets you change the value of the outer variable inside the function. – weston Aug 13 '12 at 13:36
  • @dvvrd That is incorrect. Please see my example. – Jaime Torres Aug 13 '12 at 13:38
  • For at least the C++ code part (I cannot tell if the C# is correct), it would be useful if you used actual valid C++ syntax. – PlasmaHH Aug 13 '12 at 13:48
  • @weston it's true that both `ref int` and `int *` allow you to simulate "pass by reference", but unlike C, C++ has *real* pass-by-reference baked into the language, and that is `int &`. – Michael Edenfield Aug 13 '12 at 13:50
  • @weston untrue. See Konrad Rudolph answer. Also, you can modified the "value of the outer variable inside the function" without ref, if the type of the value is a reference type. – ken2k Aug 13 '12 at 14:02
  • @weston: Pointers in both C and C++ are more similar to objects in C# than to `ref`s: With a C||C++-pointer and with a C#-object, you can: Modify the data at the place the pointer/object points at, but you can't overwrite the callers copy of the pointer. – Sebastian Mach Aug 13 '12 at 14:11

3 Answers3

12

Is this a correct analogy?

Despite what the other answers say, no. What ref means in terms of C++ actually depends on the type. For value types, your assessment is correct (or close enough). For reference types, a more suitable analogy would be a reference-to-pointer:

public void CPlusPlus(type*& bar)

The whole point about ref is that you can change the reference being passed in. And you can’t do that in C++ by simply passing a pointer:

void f(type* bar) {
    bar = new_address;
}

type* x;
f(x);

This code won’t change the caller’s value of x. If you had passed bar as type*&, on the other hand, it would have changed the value. That is what ref does.

Furthermore, a reference in C# is quite unlike a reference in C++, and much more like a pointer in C++ (in that you can change which object the reference refers to).

Konrad Rudolph
  • 530,221
  • 131
  • 937
  • 1,214
  • If `type*` is the type `T`, then the `ref` equivilant function would need to have a param type of `T*` or `type**`. Your example shows a function with an argument of plain type `T`. – weston Aug 13 '12 at 13:45
  • @weston Fair enough. But that gets even more confusing because in C++ most people would use `*&` to modify a pointer argument, not `**` (the latter is used quite often in C, though). Unfortunately, OP left out the types in his question altogether, making everything unclear. – Konrad Rudolph Aug 13 '12 at 13:48
  • Thank you, I apologize for leaving out *& and **, I should have really included them, but thank you for being able to decipher my example anyway :) – ElmsPlusPlus Aug 13 '12 at 13:56
3

It would be more accurate (though still not exactly the same) to swap your "Reference types" and "ref" examples.

In C#, a reference type is a type that is always accessed internally via a reference to an instance of the type. It is easiest to thing of these as "pointers" in the C++ sense: you allocate memory, run the constructor(s), and get back a value that indirectly refers to the object you want. The difference between C# and C++ here is that in C#, this is a property of the type, not of the variable. A type is either always a reference type or always a value type. One effect of this is that you don't have to do anything special to use a reference type (there is no "dereference" operator in managed C# code); the compiler assumes that reference type variable access is indirected. In C++ you would still need to use the -> operator, because you can have both value and reference variables of the same type (object x vs. object *x).

The ref keyword is used to pass parameters by reference; those parameters can be either a value type (like int) or a reference type. While the implementation of the ref keyword is ultimately an address-of/pointer-to type operation (exactly as & is in C++), ref (and out) produce a special type of object called a managed reference, which is different from a reference type in that you can have managed references to value types. This is almost exactly the way C++ works: an int& is a special type of "reference to an int" that is distinct from int *, even though both are basically using pointer indirection to access a variable. Similarly, in C# you can have a ref Object, which would be effectively an object *&.

Michael Edenfield
  • 28,070
  • 4
  • 86
  • 117
1

Yes, you are mostly correct, (s/*/*& and you're 100% there). As @weston mentioned, out is an additional keyword to be familiar with. The only cute thing you can do with ref is overload a function that's not ref.

class Person {
   string Name { get; set; }
   string Address { get; set; }
   int age { get; set; }
}

public void UpdateName(Person p)
{
   if (p == null) 
   {
      return;
   }

   p.Name = "Tom";
}

public void UpdateName(ref Person p)
{
   if (p == null)
   {
      p = new Person();
   }

   p.Name = "Tom";
}

Obviously useless, but it does provide for some interesting opportunities (and bad design). out does not provide the same overload functionality.

If you would like 1 for 1, you can always block your code with unsafe.

Jaime Torres
  • 10,365
  • 1
  • 48
  • 56