VBJB wrote:
So are you saying that when we are passing by value we are really passing the *address* of the object by value?
If this is true, then passing big objects by value is not that bad, but I want to clarify this first.
Someone?
No, you've got it wrong. I think you're still thinking about C++. In C++ a type is a type, and it can be instantiated on the stack or the heap, depending on how you instantiate it. In C# (and .NET in general), however, there are two different types of objects -- reference types and value types. Here's a short tutorial:
http://www.albahari.com/value%20vs%20reference%20types.htmlBasically, a reference type is instantiated on the heap and a reference (similar to a pointer, but no pointer arithmetic) to the object is stored in the stack, while a value type is instantiated on the stack. When you pass a reference type to a method, you're passing that pointer equivalent to the method. When you pass a value type to a method, a copy is pushed onto the stack and the method uses the copy. The ref and out keyword when applied to a value type are like a pointer to the type, but when applied to a reference type, they're equivalent to a pointer to a pointer.
Let me see if I can explain the differences in terms of C++ and C#:
Reference Type
In C#, a reference type is instantiated as follows:
MyObject foo = new MyObject();
The C++ equivalent:
MyObject * const foo = new MyObject();
In C++ a reference type is equivalent to a const pointer to an object. It's much safer than a pointer, however.
In C# a reference type is declared in a method as follows:
void MyMethod(MyObject foo) {...}
The C++ equivalent:
void MyMethod(MyObject * const foo) {...}
Value Type
In C#, a value type is instantiated as follows:
MyStruct foo; // uses default constructor
// or
MyStruct foo = new MyStruct(); // also uses default constructor
The C++ equivalent:
MyStruct foo;
// or
MyStruct foo(arg1, arg2);
In C++ a value type is equivalent to a stack instantiated type. In C++, any type can be stack instantiated. In C#, only value types (i.e. ints, floats, bools, and structs) can be stack instantiated.
In C# a value type is declared in a method as follows:
void MyMethod(MyStruct foo) {...}
The C++ equivalent:
void MyMethod(MyStruct foo) {...}
Again, when passing a value type, a shallow copy is pushed to the stack.
ref and out Keywords
In C#, adding the ref or the out keyword to a method parameter is very similar to adding an additional pointer to the parameter. If you add ref to a value type, it's like passing a pointer to a stack-instantiated object. If you add it to a value type, it's like passing a pointer to a const-pointer to a heap-instantiated object.
In C# a ref to a value type is declared as follows:
void MyMethod(ref MyStruct foo) {...}
The C++ equivalent:
void MyMethod(MyStruct * foo) {...}
In C# a ref to a reference type is declared as follows:
void MyMethod(ref MyObject foo) {...}the C++ equivalent:
void MyMethod(MyObject * * foo) {...}As Minh mentioned, you can use the ref keyword to re-instantiate a reference type.
So, where does that leave you? Well, if your object is a reference type object -- even a very large one -- you don't have to do anything special -- reference types are automatically passed by reference without needing the ref or out keyword.
You worried about the calling method being able to modify your object. Frankly, the bottom line is you have no real control over whether or not the calling method modifies your object; if you give it your object any method has the ability to modify it. You can make it harder to modify with tricks -- one such trick that you're used to in C++ is passing a const pointer, and you can do a similar trick in C# using a read-only interface -- but ultimately any sufficiently determined method can modify your object.
It boils down to trust. If you control the method code, you don't have to worry, just don't modify the object. Badabingbadaboom. If you don't control the method code, you have to determine your level of trust for the method.