Methods with string parameters and ref in .Net
I have interesting example, could you please explain me it? As i understand string is a reference class, but it can not be modified. So when i try to modify string - framework create new reference to new heap address. Bu i do not understand why this new开发者_StackOverflow address from method Bear do not pass outside to the main call stack.
class DoWork
{
public static void Beer(string s1) {
s1 = s1 + "beer";
}
public static void Vodka(ref string s2) {
s2 = s2 + "vodka";
}
}
string sss = "I Like ";
DoWork.Beer(sss);
DoWork.Vodka(ref sss);
so sss will have value "I Like vodka" but why?
P.S. or this way
DoWork.Vodka(ref sss);
DoWork.Beer(sss);
P.P.S. some IL code from example
DoWork.Beer:
IL_0000: ldarg.0
IL_0001: ldstr "beer"
IL_0006: call System.String.Concat
IL_000B: starg.s 00
IL_000D: ret
DoWork.Vodka:
IL_0000: ldarg.0
IL_0001: ldarg.0
IL_0002: ldind.ref
IL_0003: ldstr "vodka"
IL_0008: call System.String.Concat
IL_000D: stind.ref
IL_000E: ret
Simply put, ref
keyword enables the called method to manipulate the reference at the call site. This is the full purpose of ref
.
What happens is that when Vodka
returns, sss
has been replaced with a new string instance. It does not update the original instance. Without the ref
modifier this is not possible.
One thing that I often find that people are confused about when talking about this is when you consider the concepts of passing parameters by value and by reference, when you deal with reference types. When you pass a string (a reference type) by value, what is it really that you pass along? You pass the value of the variable. In this case, the value is a reference to a string. So you pass that value, and the calling method can use that value to access the string. However, since the parameter is passed by value, it is the value as such that is passed (essentially a copy of the reference), not the pointer to the memory location storing the value. This is why the calling method cannot replace the instance at the call site.
When passing a parameter by reference, instead of the value, the called method gets access to the memory location in which the value is stored (keep in mind now that the value is the reference to the string). This means that the called method can now update this value, to have the variable at the call site contain a reference to another string.
Update
Let's dissect the example from your comment (the names have been changed to protect the innocent):
class SomeClass
{
public int Value { get; set; }
}
class DoWork
{
public static void DoOne(SomeClass c) { c.Value = c.Value + 1; }
public static void DoTwo(ref SomeClass c) { c.Value = c.Value + 2; }
}
SomeClass
is a reference type. This means that any piece of code to which you pass such an instance (and, just to be clear, we never pass the actual instance of a reference type, we pass a reference to an instance), can manipulate data in that instance*. This means that it can call methods, set property values and so on, and any change in state that this leads to is also visible at the call site. The ref
keyword has no effect on that. So in your sample, the ref
keyword makes no difference. However, consider this change:
class DoWork
{
public static void DoOne(SomeClass c)
{
c = new SomeClass();
c.Value = 1;
}
public static void DoTwo(ref SomeClass c)
{
c = new SomeClass();
c.Value = 2;
}
}
Now, lets call it:
var temp = new SomeClass() { Value = 42 };
DoWork.DoOne(temp);
Console.WriteLine(temp.Value); // prints 42
DoWork.DoTwo(ref temp);
Console.WriteLine(temp.Value); // prints 2
What happens now is that both methods create a new SomeClass
instance to operate on. Since DoOne
gets the reference passed by value, it will update its own private copy of the reference to point to the new instance, and naturally this change is not seen at the call site.
On the other hand, since DoTwo
gets the reference passed by reference, when it creates a new instance and assigns it to c
, it updates the same memory location as call site is using, so temp
is now referencing the new instance created inside DoTwo
.
To summarize: for reference types, the only time you need to use ref
is when you want to enable the called method to replace the passed instance itself. If you only want to manipulate the state of that instance, reference types will always allow that.
Cody Gray posted a link to Jon Skeet's Parameter passing in C#, if you didn't already, go read it. It does a very good job at explaining these things (and more; it also deals with value types).
*(unless the type is immutable, like string, but that is a completely different story...)
Have a look at immutable types
http://codebetter.com/patricksmacchia/2008/01/13/immutable-types-understand-them-and-use-them
in Beer
the pointer to the string is passed by value, so when you do s1 = s1 + "beer";
the copy of the pointer is changed and it is not visible outside the method.
in Vodka
however, the pointer to the string is passed by reference, so when you do s1 = s1 + "vodka";
the same pointer is being changed and it is visible outside the method.
精彩评论