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 inject
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.
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.
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 thenew
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!
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