Why use the 'ref' keyword when passing an object?
Pass a ref
if you want to change what the object is:
TestRef t = new TestRef(); t.Something = "Foo"; DoSomething(ref t); void DoSomething(ref TestRef t) { t = new TestRef(); t.Something = "Not just a changed t, but a completely different TestRef object"; }
After calling DoSomething, t
does not refer to the original new TestRef
, but refers to a completely different object.
This may be useful too if you want to change the value of an immutable object, e.g. a string
. You cannot change the value of a string
once it has been created. But by using a ref
, you could create a function that changes the string for another one that has a different value.
Edit: As other people have mentioned. It is not a good idea to use ref
unless it is needed. Using ref
gives the method freedom to change the argument for something else, callers of the method will need to be coded to ensure they handle this possibility.
Also, when the parameter type is an object, then object variables always act as references to the object.
This means that when the ref
keyword is used you've got a reference to a reference.
This allows you to do things as described in the example given above.
But, when the parameter type is a primitive value (e.g. int
),
then if this parameter is assigned to within the method,
the value of the argument that was passed in will be changed after the method returns:
int x = 1; Change(ref x); Debug.Assert(x == 5); WillNotChange(x); Debug.Assert(x == 5); // Note: x doesn't become 10 void Change(ref int x) { x = 5; } void WillNotChange(int x) { x = 10; }
引用类型
对于引用类型的参数而言,传参进去之后,可以改变参数所指向的对象。所以只对immutable的引用类型有意义,普通的引用类型,本身就具有改变参数指向的对象的功能。
比如string的使用,虽然在方法内部设置了,但是出了方法后,并没有设置成功
String是被特殊处理过的引用类型,在函数内部处理过的话不会带到函数外部,通过使用ref string类型的参数,可以实现函数内部修改了参数,函数外部也会变化
public void ToLower(string str) { str = str.ToUpper(); }
https://stackoverflow.com/questions/1096449/c-sharp-string-reference-type
值类型
对于值类型的参数而言,如果在传参进去的方法内部改变的值类型参数的值,那么在方法返回之后,对应的值类型参数的数值是会被改变的。
ref (C# Reference)
Passing an argument by reference
When used in a method's parameter list, the ref
keyword indicates that an argument is passed by reference, not by value. The ref
keyword makes the formal parameter an alias for the argument, which must be a variable. In other words, any operation on the parameter is made on the argument. For example, if the caller passes a local variable expression or an array element access expression, and the called method replaces the object to which the ref parameter refers, then the caller's local variable or the array element now refers to the new object when the method returns.
Note
Do not confuse the concept of passing by reference with the concept of reference types. The two concepts are not the same.
A method parameter can be modified by ref
regardless of whether it is a value type or a reference type.
There is no boxing of a value type when it is passed by reference.
When to use ref and when it is not necessary in C#
Short answer: read my article on argument passing.
Long answer: when a reference type parameter is passed by value, only the reference is passed, not a copy of the object. This is like passing a pointer (by value) in C or C++. Changes to the value of the parameter itself won't be seen by the caller, but changes in the object which the reference points to will be seen.
When a parameter (of any kind) is passed by reference, that means that any changes to the parameter are seen by the caller - changes to the parameter are changes to the variable.
The article explains all of this in more detail, of course :)
Useful answer: you almost never need to use ref/out. It's basically a way of getting another return value, and should usually be avoided precisely because it means the method's probably trying to do too much. That's not always the case (TryParse
etc are the canonical examples of reasonable use of out
) but using ref/out should be a relative rarity.
How are strings passed in .NET?
To answer your question, consider the following code:
void Main()
{
string strMain = "main";
DoSomething(strMain);
Console.Write(strMain); // What gets printed?
}
void DoSomething(string strLocal)
{
strLocal = "local";
}
There are three things you need to know in order to predict what will happen here, and to understand why it does.
- Strings are reference types in C#. But this is only part of the picture.
- They are also immutable, so any time you do something that looks like you're changing the string, you aren't. A completely new string gets created, the reference is pointed at it, and the old one gets thrown away.
- Even though strings are reference types,
strMain
isn't passed by reference. It's a reference type, but the reference is being passed by value. This is a tricky distinction, but it's a crucial one. Any time you pass a parameter without theref
keyword (not countingout
parameters), you've passed something by value.
But what does that mean?
Passing reference types by value: You're already doing it
There are two groups of data types in C#: reference types and value types. There are also two ways to pass parameters in C#: by reference and by value. These sound the same and are easily confused. They are NOT the same thing!
If you pass a parameter of ANY type, and you don't use the ref
keyword, then you've passed it by value. If you've passed it by value, what you really passed was a copy.
But if the parameter was a reference type, then the thing you copied was the reference, not whatever it was pointing at.
Here's the first line of our Main
method:
string strMain = "main";
There are actually two things we've created on this line: a string with the value main
stored off in memory somewhere, and a reference variable called strMain
pointing to it.
DoSomething(strMain);
Now we pass that reference to DoSomething
. We've passed it by value, so that means we made a copy. But it's a reference type, so that means we copied the reference, not the string itself. Now we have two references that each point to the same value in memory.
Inside the callee
Here's the top of the DoSomething
method:
void DoSomething(string strLocal)
No ref
keyword, as usual. So strLocal
isn't strMain
, but they both point to the same place. If we "change" strLocal
, like this...
strLocal = "local";
...we haven't changed the stored value, per se. We've re-pointed the reference. We took the reference called strLocal
and aimed it at a brand new string. What happens to strMain
when we do that? Nothing. It's still pointing at the old string!
string strMain = "main"; //Store a string, create a reference to it
DoSomething(strMain); //Reference gets copied, copy gets re-pointed
Console.Write(strMain); //The original string is still "main"
Immutability is important
Let's change the scenario for a second. Imagine we aren't working with strings, but some mutable reference type, like a class you've created.
class MutableThing
{
public int ChangeMe { get; set; }
}
If you follow the reference objLocal
to the object it points to, you can change its properties:
void DoSomething(MutableThing objLocal)
{
objLocal.ChangeMe = 0;
}
There's still only one MutableThing
in memory, and both the copied reference and the original reference still point to it. The properties of the MutableThing
itself have changed:
void Main()
{
var objMain = new MutableThing();
objMain.ChangeMe = 5;
Console.Write(objMain.ChangeMe); //it's 5 on objMain
DoSomething(objMain); //now it's 0 on objLocal
Console.Write(objMain.ChangeMe); //it's also 0 on objMain
}
Ah, but...
...strings are immutable! There's no ChangeMe
property to set. You can't do strLocal[3] = 'H';
like you could with a C-style char array; you have to construct a whole new string instead. The only way to change strLocal
is to point the reference at another string, and that means nothing you do to strLocal
can affect strMain
. The value is immutable, and the reference is a copy.
So even though strings are reference types, passing them by value means whatever goes on in the callee won't affect the string in the caller. But since they are reference types, you don't have to copy the entire string in memory when you want to pass it around.
Further resources:
- Here is the best article I've read on the difference between reference types and value types in C#, and why a reference type isn't the same as a reference-passed parameter.
- As usual, Eric Lippert also has several excellent blog posts on the subject.
- He has some great stuff on immutability, too.