Reputation: 175
I made a function and I was to pass array of strings in it like string[] aa= null
In function definition I made some update in it and then I found that its value was not updated when function was executed/returned .I am unable to understand which things in C# we are required to mention ref keyword . Doesn't it always pass parameters by reference why need to mention ref ? which are the objects which require to be mentioned with ref in order to pass by reference ?
Code was like this ,just trying to show what was I doing,I was unable to update value without use of ref .
string[] temp = null
foo(ref temp);
//function definition
void foo (ref string[] temp)
{
temp = {"Hello World ","You must be updated now"}
}
foreach(string s in temp)
System.Console.WriteLine(s)
Upvotes: 1
Views: 415
Reputation: 6026
First, you're pseudo code should work. But, before we get to that, there are three things at play here: value types, reference types, and the "ref" keyword.
Value types are typically your simple basic types like int, double, etc. string is a weird one as it is considered a value type.
More complex types, such as arrays and classes are reference types.
When you pass value types like ints and doubles then you are passing a copy of the value so if you pass int x = 10 into a method a change to x in the method won't be reflected once you leave the method. On the other hand, if you pass MyClass class1, any changes to the properties within class1 will be reflected outside the function. Just don't try to new up a new class1 inside your method because that won't change outside the caller.
If you want to change a value type within a method, you pass by ref. If you want to new up a new class or array, then you pass by ref.
One more thing: it's not that black-and-white between using out vs. ref. You would use out only if the design for the method was to always create your class or array only inside the method. You would use ref on a reference type if you wanted to allow the possibility to create a new object. Like,
//function definition
void foo (ref string[] temp)
{
if(temp == null)
{
temp = new string[] { "Hello World ", "You must be updated now" };
}
else
{
// do something with the existing temp
}
}
Finally, if this is your actual code:
string[] temp = null;
foo(ref temp);
foreach (string s in temp)
System.Console.WriteLine(s);
Later:
//function definition
void foo (ref string[] temp)
{
temp = new string[] { "Hello World ", "You must be updated now" };
}
Then, it should actually work.
Upvotes: 1
Reputation: 269348
C# always passes parameters by value, unless you use the ref
or out
modifiers.
string[] temp = { "zero", "one", "two" };
MutateByVal(temp);
MutateByRef(ref temp);
void MutateByVal(string[] arr)
{
// arr and temp are separate references to the same array
// the value of arr (a reference) is a copy of the value of temp
// mutate the array referenced by arr
arr[1] = "mutated!";
// arr and temp still point at the same array
// so both arr and temp now contain { "zero", "mutated!", "two" }
// re-assign arr
arr = new[] { "blah", "blah", "blah" };
// arr and temp now point at different arrays
// arr now contains { "blah", "blah", "blah" }
// temp still contains { "zero", "mutated!", "two" }
}
void MutateByRef(ref string[] arr)
{
// arr is an alias for temp
// that is, they are two different names for the same reference
// mutate the array referenced by arr
arr[1] = "mutated!";
// arr and temp are the same reference
// so both arr and temp now contain { "zero", "mutated!", "two" }
// re-assign arr
arr = new[] { "blah", "blah", "blah" };
// arr and temp are the same reference
// so both arr and temp now contain { "blah", "blah", "blah" }
}
Upvotes: 2
Reputation: 42333
Without the ref
the reference to the array is simply copied and passed to the method (by value - since a reference type's value is the reference to the object); allowing the method to access the same array, but it does not allow it to modify the caller's own reference.
That's what the ref
(or indeed out
) keyword is for.
However - you do not need to use ref
if you simply want the method to alter the object that is referenced (depending on whether the type can be altered after construction - i.e. if it is immutable or not).
So this method will overwrite the first element of the passed array:
public static void foo(string[] ss)
{
if(ss!=null && ss.Length > 0)
ss[0] = "Overwritten";
}
public static void main()
{
var strings = new[] { String.Empty, "Original" };
foo(strings);
Console.WriteLine(strings[0]); //will print 'Overwritten'.
}
What foo
cannot do in the above code, however, is new
the ss
parameter and expect that to alter the value of the reference strings
passed as the argument from main
.
To do, we need to pass a reference to the strings
local, which is where ref
comes in. If we do that - then strings
in the main()
can be altered, if passed null, to point to a new array:
public static void foo(ref string[] ss)
{
if(ss==null || ss.Length == 0)
ss= new string[1];
ss[0] = "Overwritten";
}
public static void main()
{
string[] strings = null
foo(ref strings);
Console.WriteLine(strings[0]); //will still print 'Overwritten'.
}
Upvotes: 1
Reputation: 67075
You are a little mistaken in the intricacies. If you pass a reference object, then it is passes the reference by value (keep reading and hopefully it should begin to make more sense). However, that does not mean it is pass by ref. In fact, C# parameters are passed by value by default.
Here is a good article that explains this pretty well
And, this is the snippet that explains what I mean when I say you are passing the reference by value
In C#, parameters are (by default) passed by value, meaning that they are implicitly copied when passed to the method. For value-type parameters, this means physically copying the instance (in the same way p2 was copied), while for reference-types it means copying a reference (in the same way f2 was copied)
. However, in your case, you are passing in your object and then creating a new object with a new reference within your method. So, the original reference remains intact, you can see this by performing an update instead of a totally new creation. When you explicitly say ref, then you are now passing a reference to the reference, so it works because you are only working with the pointer to the reference, and when you create the new object it will be placed into that reference location.
As eouw0o83hf mentioned, if you are creating a totally new object, then you should use an out
to signify this. ref
is usually more for value objects, which are not passed by reference.
To summarize:
ref
ref
or out
)out
UPDATE
Here is an MSDN article explaining exactly what you are asking :)
Upvotes: 4
Reputation: 35905
Any type passed in C# as a parameter is passed by value. So if this is an array of strings C# makes a copy of a reference (this is a reference type) and pass it in. But because this is a reference type, both variables (from inner method scope and outer parameter) would point to the same object in a managed heap (or large object heap).
The problem with your code is that you create a new variable (with incorrect syntax). To fix this you just use an indexer of an array directly, i.e.:
void foo (string[] temp) // create a copy of a reference to the string array
{
temp[0] = "Boom"; // temp still points to the same object
}
-------------
string[] temp = new [] {"one", "two", "three"}; //outer variable
foo(temp); // behind the scene we have two variables pointing to the same array
foreach (string s in temp)
System.Console.WriteLine(s);
Upvotes: 1
Reputation: 9588
It's because you're actually returning a whole new object rather than modifying an entry on an existing one. If you are assigning a new object, you should use out
instead.
Here's an example showing how out
, ref
, and regular passing will work on an array arg. As you can see, the array is passed by reference whether or not ref
is specified; however, if you return a whole new object, you need to specify out
:
class Program
{
static void Main(string[] args)
{
string[] val;
foo(out val);
Console.WriteLine(string.Join(",", val));
// Output: 1, 2
bar(ref val);
Console.WriteLine(string.Join(",", val));
// Output: modified, 2
bar2(val);
Console.WriteLine(string.Join(",", val));
// Output: modified again, 2
Console.Read();
}
static void foo(out string[] temp)
{
temp = new string[]{"1", "2"};
}
static void bar(ref string[] temp)
{
temp[0] = "modified";
}
static void bar2(string[] temp)
{
temp[0] = "modified again";
}
}
Upvotes: 2
Reputation: 81143
Think of a class reference as being an "object ID". If one has a variable MyCar
of class type Car
, the variable doesn't actually hold a car. Instead, it holds the "object ID" of a car which is actually stored elsewhere. If MyCar
holds "#1543", the statement MyCar.Color = CarColors.Purple;
won't actually modify variable MyCar
. Instead, it will tell the system "Object #1543 should be a Car
. Set its Color
property to CarColors.Purple." In many cases, a routine which passes a variable of type
Carwill simply want the called code to do something with the
Caridentified by that variable. In a few cases, however, one may be necessary to let the called code change the object ID stored within
MyCaritself, so that it points to an entirely different instance of
Car`.
In your particular case, the object in question is an array. The called routine creates a whole new array, but the caller starts out with temp
equal to null. The only way the caller is going to see the new array is if a reference to it gets stored into temp
. Otherwise the caller will continue to see whatever array temp
had before (or null
, if no array reference was stored there).
Upvotes: 1