views:

313

answers:

4
+6  Q: 

Passing by ref?

I am still confused about passing by ref.

If I have a Cache object which I want to be accessed/available to a number of objects, and I inject it using constructor injection. I want it to affect the single cache object I have created. eg.

public class Cache {

   public void Remove(string fileToRemove) {
      ...
   }
}

public class ObjectLoader {

   private Cache _Cache;

   public ObjectLoader(Cache cache) {

   }

   public RemoveFromCacheFIleThatHasBeenDeletedOrSimilarOperation(string filename) {
      _Cache.Remove(fileName);
   }
}

Should I be using ref when I pass the Cache into the ObjectLoader constructor?

+5  A: 

Use the 'ref' keyword when the you need to modify what the reference is pointing to. When you pass a reference type into a method it is passed by value, but the value is a copy of that reference which is passed to the method. This means that you can change the general state (i.e., properties/fields) of the referred to object, but if you attempt to change what the reference points to you will only affect the copy.

For example, given this method...

private void Foo( MyClass obj )
{
    obj = new MyClass( );
    obj.SomeProperty = true;
}

We can pass in the argument and then see if it was affected:

MyClass test = new MyClass( );
test.SomeProperty = false;
Foo( test );
Console.WriteLine( test.SomeProperty );  // prints "False"

Now, if we had defined the method using the 'ref' keyword...

private void Foo( ref MyClass obj )
{
    obj = new MyClass( );
    obj.SomeProperty = true;
}

The output would be "True", because the actual reference was passed to the method, not a copy. We changed what that reference points to within the function and we see the effects of those changes.

You are just creating a new pointer to the object on the heap when you omit the 'ref' keyword. If you change one pointer you will not change the other.

...

So, to answer your question; no, you do not need to use the 'ref' keyword to change the state of your single Cache object when it is passed to a method.

Ed Swangren
It is so much simpler with normal C/C++ pointers :)
Eugene
Stormenet
nitpick: 2nd example - you are accessing a static property.
Cherian
Oh, thanks. [15chars]
Ed Swangren
A: 

Objects are automatically passed by reference even when you declare them to be passed by value in function arguments, in the .NET framework.

This is because the object itself is a reference type, so you can modify the members of the object, even though you cannot replace the object itself.

See

http://msdn.microsoft.com/en-us/library/aa903253(VS.71).aspx

Larry Watanabe
Objects are passed by value, just like everything else. The difference with objects (reads: reference types) is that the REFERENCE is passed by value.
Matthew Scharley
All parameters are passed by value in C# unless you explicitly use ref or out. However, in the case of reference types the copied reference still points to the same instance so you can modify the instance, but not the reference itself.
Brian Rasmussen
+9  A: 

No you do not need to use the ref keyword in this situation.

Cache is a class, it is a reference type. When a reference is passed into a method, a copy of the reference (not the object itself) is placed into your parameter. Both references, inside and outside, of the method are pointing to the same object on the heap, and modification of the object's fields using one will be reflected in the other.

Adding ref to your method call passes in the original reference. This is useful in a situation where you would be reassigning (ie. by calling new) the location the reference points to from within a calling method.

statenjason
+1  A: 

I think you're wondering how many copies of the Cache object will get created. You only want one copy to be shared by multiple client objects. Well, there's a very simple rule you can remember in C#, whenever you want to know how many separate copies of your object will be created.

If the object's type is declared with the class keyword, then there is only one way to make a new instance of it: with the new keyword.

There are minor exceptions to this: you can call BCL methods that create objects, but the point is that it is explicit. You have to specifically ask for it to happen. The language will not automatically make copies of class objects.

So in your example, you have a class called Cache, and so you know for certain that you can pass around variables of type Cache as much as you like, and no further copies of Cache will be created. All the variables that have that object assigned to them will be "pointing" to the same original object. This is because a Cache variable doesn't store the object itself, but only the location of a Cache object in memory.

Contrast this with what happens if you declare a struct type instead of a class. Now when you declare a variable of that type, the variable itself has to be large enough to store all the data declared in the struct. Every variable is a separate copy. Every parameter is a separate copy.

You can override this by adding the ref keyword, but it's a pretty unusual keyword in most programs. The out keyword is more common, and is best thought of as a way to give a method more than one return value.

What effect does ref have on a variable if it is of class type? In your example:

public ObjectLoader(Cache cache) {
    // do stuff with cache (store it?)
}

I could construct two object loaders like this:

Cache c = new Cache();
ObjectLoader a = new ObjectLoader(c), 
ObjectLoader b = new ObjectLoader(c);

How many objects did we just create? Simply count the new keywords. Now, suppose we added the ref keyword:

public ObjectLoader(ref Cache cache) {

    _cache = cache; // store        

    // do something very odd!
    cache = new Cache();
}

Hidden inside that constructor, I've created another cache, and stored it in the parameter I was passed. Because it's a ref parameter, I've affected the caller's variable! So in the calling code:

Cache c = new Cache();
ObjectLoader a = new ObjectLoader(ref c), 
ObjectLoader b = new ObjectLoader(ref c);

Now we have five uses of new: three in the above snippet, plus two calls to the modified ObjectLoader constructor. Each time ObjectLoader's constructor is called, we pass it c. We have to put the ref keyword, which is a very good thing because it lets the person reading the code know that something strange is going on. The variable c points to a different Cache after ObjectLoader's constructor returns. So b's ObjectLoader ends up storing a pointer to a different Cache to a!

Needless to say, this would be quite a messy pattern for the code to have. It would be even worse if we didn't have to put the ref keyword at the calling site!

Daniel Earwicker