1

I am new to C# and I have been messing around with 'ref', 'out' and pointers, and I have a general question about how 'ref' works, especially when using objects and not primitive types. Say this is my method:

public void foo(ref Point p) {
     p.set(1,1);   // the x/y values are updated without constructing a new Point
}

and a similar method:

public void bar(Point p) {
     p.set(1,1);   // the x/y values are updated without constructing a new Point
}

EDIT: Point is a class in both cases

Both work, but is one more cost effective than the other? I know in C++ if you pass in a pointer you are only giving the memory address; from my understanding of C#, you cannot pass in an Object* into a method because of the automatic garbage collection. Does 'ref' pin an object to a location? Also, if you pass in an object to a method, like 'bar' above, is it passing a copy of the object or is it passing a pointer/reference?

Clarification: In my book I have, it does say if you want a method to update a primitive, such as int, you need to use ref (out if it is not initialized) or a *. I was asking if the same holds true for objects, and if passing an object as a parameter rather than a ref to an object costs more.

Wai Ha Lee
  • 8,598
  • 83
  • 57
  • 92
Jedi_Maseter_Sam
  • 753
  • 4
  • 24
  • What is the type of `Point` in your example? If `Point` is a class type then the changes to `p` will be visible after the call to `foo` completes, but if `Point` is a `struct` then without `ref` the changes will not be effected in a way the upstream caller sees. – Dai Apr 10 '16 at 02:17
  • `Point` is a class. Is it like Java in this situation, and `Object` comes bundled with a pointer? – Jedi_Maseter_Sam Apr 10 '16 at 02:21
  • You don't pass an object. You pass a reference to the object in both cases. – Brian Rasmussen Apr 10 '16 at 02:32
  • So both `ref` and `*` are only useful for changing primitive types? (when they are parameters of methods) – Jedi_Maseter_Sam Apr 10 '16 at 02:34
  • Possible duplicate of [C# parameters by reference and .net garbage collection](http://stackoverflow.com/questions/2500851/c-sharp-parameters-by-reference-and-net-garbage-collection) – AGB Apr 10 '16 at 02:47
  • I am asking whether or not `ref` is needed and/or is more cost effective when passing an object you wish to update via a method. – Jedi_Maseter_Sam Apr 10 '16 at 02:58

3 Answers3

4
  • If your type is a struct, ref is roughly equivalent to a pointer to that struct. No new instances are created here. The difference (from passing it without ref) is that you can now mutate the original struct instance contained in that variable.
  • If your type is a class, ref simply adds one more level of indirection. No new instances are created here either. The difference (from passing it without ref) is that you can now entirely replace (not just mutate) the original class instance referenced by that variable with something else.

Since no new instances are created in either case, the garbage collector probably won't care about this in any important way.

Theodoros Chatzigiannakis
  • 28,773
  • 8
  • 68
  • 104
1

Quick distinction between a class vs. a struct:

A class is a reference type. When an object of the class is created, the variable to which the object is assigned holds only a reference to that memory. When the object reference is assigned to a new variable, the new variable refers to the original object. Changes made through one variable are reflected in the other variable because they both refer to the same data.

A struct is a value type. When a struct is created, the variable to which the struct is assigned holds the struct's actual data. When the struct is assigned to a new variable, it is copied. The new variable and the original variable therefore contain two separate copies of the same data. Changes made to one copy do not affect the other copy.

https://msdn.microsoft.com/en-us/library/ms173109.aspx

Your example is tricky because in c# Point is an immutable struct, not an object.

Hopefully this example will help show what happens with structs and objects with and without ref.

public static void StructTest()
{
    var fooStruct = new MyStruct();
    var barStruct = new MyStruct();

    Console.WriteLine(fooStruct.Value); // prints 0
    Console.WriteLine(barStruct.Value); // prints 0

    fooStruct(ref fooStruct);
    barStruct(barStruct);

    // Struct value only changes when passed by reference.
    Console.WriteLine(fooStruct.Value); // prints 1
    Console.WriteLine(barStruct.Value); // prints 0    
}

public void fooStruct(ref MyStruct m) 
{
    m.Value++;
}

public void barStruct(MyStruct m)
{
    m.Value++;
}

public static void ObjectTest()
{
    var fooObject = new MyObject();
    var barObject = new MyObject();

    Console.WriteLine(fooObject.Value); // prints 0
    Console.WriteLine(barObject.Value); // prints 0

    fooObject(ref fooObject);
    barObject(barObject);

    // Objects are automatically passed by reference. No difference.
    Console.WriteLine(fooObject.Value); // prints 1
    Console.WriteLine(barObject.Value); // prints 1

    fooSetObjectToNull(ref fooObject);
    barSetObjectToNull(barObject);

    // Reference is actually a pointer to the variable that holds a reference to the object.
    Console.WriteLine(fooObject == null); // prints true
    Console.WriteLine(barObject == null); // prints false
}   

public void fooObject(ref MyObject m)
{
    m.Value++;
}

public void barObject(ref MyObject m)
{
    m.Value++;
}

public void fooSetObjectToNull(ref MyObject m)
{
    m = null;
}

public void barSetObjectToNull(MyObject m)
{
    m = null;
}
Tim Wilson
  • 365
  • 3
  • 10
  • Thank you, I get it now. If I want to update a variable in a class, the `ref` is not needed, but if I want to reinitialize it, `ref` is needed. – Jedi_Maseter_Sam Apr 10 '16 at 03:15
1

In fact, class is a reference type, it mean that a variable of a reference type hold a reference to it's data instead of holding is data directly like value type.

When you pass a variable of a reference type as method parameter, it pass the reference to that data, not the data itself. So if update some properties of your object, the update is reflected in the original variable, except if you reassign the parameter.

Example from MSDN :

class PassingRefByVal 
{
    static void Change(int[] pArray)
    {
        pArray[0] = 888;  // This change affects the original element.
        pArray = new int[5] {-3, -1, -2, -3, -4};   // This change is local.
        System.Console.WriteLine("Inside the method, the first element is: {0}", pArray[0]);
    }

    static void Main() 
    {
        int[] arr = {1, 4, 5};
        System.Console.WriteLine("Inside Main, before calling the method, the first element is: {0}", arr [0]);

        Change(arr);
        System.Console.WriteLine("Inside Main, after calling the method, the first element is: {0}", arr [0]);
    }
}
/* Output:
    Inside Main, before calling the method, the first element is: 1
    Inside the method, the first element is: -3
    Inside Main, after calling the method, the first element is: 888
*/

Passing a variable of a reference type with the ref keyword will reflect any change to the original variable, even if you reassin the parameter.

Example from MSDN :

class PassingRefByRef 
{
    static void Change(ref int[] pArray)
    {
        // Both of the following changes will affect the original variables:
        pArray[0] = 888;
        pArray = new int[5] {-3, -1, -2, -3, -4};
        System.Console.WriteLine("Inside the method, the first element is: {0}", pArray[0]);
    }

    static void Main() 
    {
        int[] arr = {1, 4, 5};
        System.Console.WriteLine("Inside Main, before calling the method, the first element is: {0}", arr[0]);

        Change(ref arr);
        System.Console.WriteLine("Inside Main, after calling the method, the first element is: {0}", arr[0]);
    }
}
/* Output:
    Inside Main, before calling the method, the first element is: 1
    Inside the method, the first element is: -3
    Inside Main, after calling the method, the first element is: -3
*/

MSDN documentation.

Fabien ESCOFFIER
  • 4,751
  • 1
  • 20
  • 32
  • This is not correct. Parameters are passed by value unless you use `ref` or `out`. Please see https://msdn.microsoft.com/en-us/library/0f66670z.aspx – Brian Rasmussen Apr 10 '16 at 04:47
  • Reference type is always passed by reference, except that you cannot update the reference itself, please see https://msdn.microsoft.com/en-us/library/s6938f28.aspx – Fabien ESCOFFIER Apr 10 '16 at 04:51
  • 5
    Introducing your own terminology that uses the same terms as the standard terminology but gives them different definitions is not necessarily invalid, but sure to cause a lot of unnecessary confusion when you then use your definitions for answering questions here. What you call "by reference" is very clearly not what MSDN calls "by reference". The fact that you refer to an MSDN article that contains "When you pass a reference-type parameter by value" to support your claim that "Reference type is always passed by reference" is a giant red flag. –  Apr 10 '16 at 08:35
  • You clearly toying with my words, but that's ok, i'll update my answer to clear the confusion. – Fabien ESCOFFIER Apr 10 '16 at 08:52
  • 1
    I wasn't toying, I really did find it confusing for exactly the reason I commented. It looks better now, I don't spot any obvious problems after a quick re-read. I don't think it answers the literal question the OP asked, but given that the answer that the OP ended up accepting doesn't do so either, that must not really be what the OP was after. –  Apr 10 '16 at 09:41