Reputation: 813
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.
Upvotes: 1
Views: 1254
Reputation: 395
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;
}
Upvotes: 1
Reputation: 4911
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.
Upvotes: 1
Reputation: 29213
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. 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.
Upvotes: 4