I know that "string" in C# is a reference type. This is on MSDN. However, this code doesn't work as it should then:
class Test
{
public static void Main()
{
string test = "before passing";
Console.WriteLine(test);
TestI(test);
Console.WriteLine(test);
}
public static void TestI(string test)
{
test = "after passing";
}
}
The output should be "before passing" "after passing" since I'm passing the string as a parameter and it being a reference type, the second output statement should recognize that the text changed in the TestI method. However, I get "before passing" "before passing" making it seem that it is passed by value not by ref. I understand that strings are immutable, but I don't see how that would explain what is going on here. What am I missing? Thanks.
The reference to the string is passed by value. There's a big difference between passing a reference by value and passing an object by reference. It's unfortunate that the word "reference" is used in both cases.
If you do pass the string reference by reference, it will work as you expect:
using System;
class Test
{
public static void Main()
{
string test = "before passing";
Console.WriteLine(test);
TestI(ref test);
Console.WriteLine(test);
}
public static void TestI(ref string test)
{
test = "after passing";
}
}
Now you need to distinguish between making changes to the object which a reference refers to, and making a change to a variable (such as a parameter) to let it refer to a different object. We can't make changes to a string because strings are immutable, but we can demonstrate it with a StringBuilder instead:
using System;
using System.Text;
class Test
{
public static void Main()
{
StringBuilder test = new StringBuilder();
Console.WriteLine(test);
TestI(test);
Console.WriteLine(test);
}
public static void TestI(StringBuilder test)
{
// Note that we're not changing the value
// of the "test" parameter - we're changing
// the data in the object it's referring to
test.Append("changing");
}
}
See my article on parameter passing for more details.
If we have to answer the question: String is a reference type and it behaves as a reference. We pass a parameter that holds a reference to, not the actual string. The problem is in the function:
public static void TestI(string test)
{
test = "after passing";
}
The parameter test holds a reference to the string but it is a copy. We have two variables pointing to the string. And because any operations with strings actually create a new object, we make our local copy to point to the new string. But the original test variable is not changed.
The suggested solutions to put ref in the function declaration and in the invocation work because we will not pass the value of the test variable but will pass just a reference to it. Thus any changes inside the function will reflect the original variable.
I want to repeat at the end: String is a reference type but since its immutable the line test = "after passing"; actually creates a new object and our copy of the variable test is changed to point to the new string.
As others have stated, the String type in .NET is immutable and it's reference is passed by value.
In the original code, as soon as this line executes:
test = "after passing";
then test is no longer referring to the original object. We've created a new String object and assigned test to reference that object on the managed heap.
I feel that many people get tripped up here since there's no visible formal constructor to remind them. In this case, it's happening behind the scenes since the String type has language support in how it is constructed.
Hence, this is why the change to test is not visible outside the scope of the TestI(string) method - we've passed the reference by value and now that value has changed! But if the String reference were passed by reference, then when the reference changed we will see it outside the scope of the TestI(string) method.
Either the ref or out keyword are needed in this case. I feel the out keyword might be slightly better suited for this particular situation.
class Program
{
static void Main(string[] args)
{
string test = "before passing";
Console.WriteLine(test);
TestI(out test);
Console.WriteLine(test);
Console.ReadLine();
}
public static void TestI(out string test)
{
test = "after passing";
}
}
"A picture is worth a thousand words".
I have a simple example here, it's similar to your case.
string s1 = "abc";
string s2 = s1;
s1 = "def";
Console.WriteLine(s2);
// Output: abc
This is what happened:
Line 1 and 2: s1 and s2 variables reference to the same "abc" string object.
Line 3: Because strings are immutable, so the "abc" string object does not modify itself (to "def"), but a new "def" string object is created instead, and then s1 references to it.
Line 4: s2 still references to "abc" string object, so that's the output.
Actually it would have been the same for any object for that matter i.e. being a reference type and passing by reference are 2 different things in c#.
This would work, but that applies regardless of the type:
public static void TestI(ref string test)
Also about string being a reference type, its also a special one. Its designed to be immutable, so all of its methods won't modify the instance (they return a new one). It also has some extra things in it for performance.
Here's a good way to think about the difference between value-types, passing-by-value, reference-types, and passing-by-reference:
A variable is a container.
A value-type variable contains an instance.
A reference-type variable contains a pointer to an instance stored elsewhere.
Modifying a value-type variable mutates the instance that it contains.
Modifying a reference-type variable mutates the instance that it points to.
Separate reference-type variables can point to the same instance.
Therefore, the same instance can be mutated via any variable that points to it.
A passed-by-value argument is a new container with a new copy of the content.
A passed-by-reference argument is the original container with its original content.
When a value-type argument is passed-by-value:
Reassigning the argument's content has no effect outside scope, because the container is unique.
Modifying the argument has no effect outside scope, because the instance is an independent copy.
When a reference-type argument is passed-by-value:
Reassigning the argument's content has no effect outside scope, because the container is unique.
Modifying the argument's content affects the external scope, because the copied pointer points to a shared instance.
When any argument is passed-by-reference:
Reassigning the argument's content affects the external scope, because the container is shared.
Modifying the argument's content affects the external scope, because the content is shared.
In conclusion:
A string variable is a reference-type variable. Therefore, it contains a pointer to an instance stored elsewhere.
When passed-by-value, its pointer is copied, so modifying a string argument should affect the shared instance.
However, a string instance has no mutable properties, so a string argument cannot be modified anyway.
When passed-by-reference, the pointer's container is shared, so reassignment will still affect the external scope.
Above answers are helpful, I'd just like to add an example that I think is demonstrating clearly what happens when we pass parameter without the ref keyword, even when that parameter is a reference type:
MyClass c = new MyClass(); c.MyProperty = "foo";
CNull(c); // only a copy of the reference is sent
Console.WriteLine(c.MyProperty); // still foo, we only made the copy null
CPropertyChange(c);
Console.WriteLine(c.MyProperty); // bar
private void CNull(MyClass c2)
{
c2 = null;
}
private void CPropertyChange(MyClass c2)
{
c2.MyProperty = "bar"; // c2 is a copy, but it refers to the same object that c does (on heap) and modified property would appear on c.MyProperty as well.
}
For curious minds and to complete the conversation:
Yes, String is a reference type:
unsafe
{
string a = "Test";
string b = a;
fixed (char* p = a)
{
p[0] = 'B';
}
Console.WriteLine(a); // output: "Best"
Console.WriteLine(b); // output: "Best"
}
But note that this change only works in an unsafe block! because Strings are immutable (From MSDN):
The contents of a string object cannot be changed after the object is
created, although the syntax makes it appear as if you can do this.
For example, when you write this code, the compiler actually creates a
new string object to hold the new sequence of characters, and that new
object is assigned to b. The string "h" is then eligible for garbage
collection.
string b = "h";
b += "ello";
And keep in mind that:
Although the string is a reference type, the equality operators (== and
!=) are defined to compare the values of string objects, not
references.
Try:
public static void TestI(ref string test)
{
test = "after passing";
}
I believe your code is analogous to the following, and you should not have expected the value to have changed for the same reason it wouldn't here:
public static void Main()
{
StringWrapper testVariable = new StringWrapper("before passing");
Console.WriteLine(testVariable);
TestI(testVariable);
Console.WriteLine(testVariable);
}
public static void TestI(StringWrapper testParameter)
{
testParameter = new StringWrapper("after passing");
// this will change the object that testParameter is pointing/referring
// to but it doesn't change testVariable unless you use a reference
// parameter as indicated in other answers
}
Another way to bypass the string behavior. Use string array of ONE element only and manipulate this element.
class Test
{
public static void Main()
{
string[] test = new string[1] {"before passing"};
Console.WriteLine(ref test);
TestI(test);
Console.WriteLine(ref test);
}
public static void TestI(ref string[] test)
{
test[0] = "after passing";
}
}
Related
I know that "string" in C# is a reference type. This is on MSDN. However, this code doesn't work as it should then:
class Test
{
public static void Main()
{
string test = "before passing";
Console.WriteLine(test);
TestI(test);
Console.WriteLine(test);
}
public static void TestI(string test)
{
test = "after passing";
}
}
The output should be "before passing" "after passing" since I'm passing the string as a parameter and it being a reference type, the second output statement should recognize that the text changed in the TestI method. However, I get "before passing" "before passing" making it seem that it is passed by value not by ref. I understand that strings are immutable, but I don't see how that would explain what is going on here. What am I missing? Thanks.
The reference to the string is passed by value. There's a big difference between passing a reference by value and passing an object by reference. It's unfortunate that the word "reference" is used in both cases.
If you do pass the string reference by reference, it will work as you expect:
using System;
class Test
{
public static void Main()
{
string test = "before passing";
Console.WriteLine(test);
TestI(ref test);
Console.WriteLine(test);
}
public static void TestI(ref string test)
{
test = "after passing";
}
}
Now you need to distinguish between making changes to the object which a reference refers to, and making a change to a variable (such as a parameter) to let it refer to a different object. We can't make changes to a string because strings are immutable, but we can demonstrate it with a StringBuilder instead:
using System;
using System.Text;
class Test
{
public static void Main()
{
StringBuilder test = new StringBuilder();
Console.WriteLine(test);
TestI(test);
Console.WriteLine(test);
}
public static void TestI(StringBuilder test)
{
// Note that we're not changing the value
// of the "test" parameter - we're changing
// the data in the object it's referring to
test.Append("changing");
}
}
See my article on parameter passing for more details.
If we have to answer the question: String is a reference type and it behaves as a reference. We pass a parameter that holds a reference to, not the actual string. The problem is in the function:
public static void TestI(string test)
{
test = "after passing";
}
The parameter test holds a reference to the string but it is a copy. We have two variables pointing to the string. And because any operations with strings actually create a new object, we make our local copy to point to the new string. But the original test variable is not changed.
The suggested solutions to put ref in the function declaration and in the invocation work because we will not pass the value of the test variable but will pass just a reference to it. Thus any changes inside the function will reflect the original variable.
I want to repeat at the end: String is a reference type but since its immutable the line test = "after passing"; actually creates a new object and our copy of the variable test is changed to point to the new string.
As others have stated, the String type in .NET is immutable and it's reference is passed by value.
In the original code, as soon as this line executes:
test = "after passing";
then test is no longer referring to the original object. We've created a new String object and assigned test to reference that object on the managed heap.
I feel that many people get tripped up here since there's no visible formal constructor to remind them. In this case, it's happening behind the scenes since the String type has language support in how it is constructed.
Hence, this is why the change to test is not visible outside the scope of the TestI(string) method - we've passed the reference by value and now that value has changed! But if the String reference were passed by reference, then when the reference changed we will see it outside the scope of the TestI(string) method.
Either the ref or out keyword are needed in this case. I feel the out keyword might be slightly better suited for this particular situation.
class Program
{
static void Main(string[] args)
{
string test = "before passing";
Console.WriteLine(test);
TestI(out test);
Console.WriteLine(test);
Console.ReadLine();
}
public static void TestI(out string test)
{
test = "after passing";
}
}
"A picture is worth a thousand words".
I have a simple example here, it's similar to your case.
string s1 = "abc";
string s2 = s1;
s1 = "def";
Console.WriteLine(s2);
// Output: abc
This is what happened:
Line 1 and 2: s1 and s2 variables reference to the same "abc" string object.
Line 3: Because strings are immutable, so the "abc" string object does not modify itself (to "def"), but a new "def" string object is created instead, and then s1 references to it.
Line 4: s2 still references to "abc" string object, so that's the output.
Actually it would have been the same for any object for that matter i.e. being a reference type and passing by reference are 2 different things in c#.
This would work, but that applies regardless of the type:
public static void TestI(ref string test)
Also about string being a reference type, its also a special one. Its designed to be immutable, so all of its methods won't modify the instance (they return a new one). It also has some extra things in it for performance.
Here's a good way to think about the difference between value-types, passing-by-value, reference-types, and passing-by-reference:
A variable is a container.
A value-type variable contains an instance.
A reference-type variable contains a pointer to an instance stored elsewhere.
Modifying a value-type variable mutates the instance that it contains.
Modifying a reference-type variable mutates the instance that it points to.
Separate reference-type variables can point to the same instance.
Therefore, the same instance can be mutated via any variable that points to it.
A passed-by-value argument is a new container with a new copy of the content.
A passed-by-reference argument is the original container with its original content.
When a value-type argument is passed-by-value:
Reassigning the argument's content has no effect outside scope, because the container is unique.
Modifying the argument has no effect outside scope, because the instance is an independent copy.
When a reference-type argument is passed-by-value:
Reassigning the argument's content has no effect outside scope, because the container is unique.
Modifying the argument's content affects the external scope, because the copied pointer points to a shared instance.
When any argument is passed-by-reference:
Reassigning the argument's content affects the external scope, because the container is shared.
Modifying the argument's content affects the external scope, because the content is shared.
In conclusion:
A string variable is a reference-type variable. Therefore, it contains a pointer to an instance stored elsewhere.
When passed-by-value, its pointer is copied, so modifying a string argument should affect the shared instance.
However, a string instance has no mutable properties, so a string argument cannot be modified anyway.
When passed-by-reference, the pointer's container is shared, so reassignment will still affect the external scope.
Above answers are helpful, I'd just like to add an example that I think is demonstrating clearly what happens when we pass parameter without the ref keyword, even when that parameter is a reference type:
MyClass c = new MyClass(); c.MyProperty = "foo";
CNull(c); // only a copy of the reference is sent
Console.WriteLine(c.MyProperty); // still foo, we only made the copy null
CPropertyChange(c);
Console.WriteLine(c.MyProperty); // bar
private void CNull(MyClass c2)
{
c2 = null;
}
private void CPropertyChange(MyClass c2)
{
c2.MyProperty = "bar"; // c2 is a copy, but it refers to the same object that c does (on heap) and modified property would appear on c.MyProperty as well.
}
For curious minds and to complete the conversation:
Yes, String is a reference type:
unsafe
{
string a = "Test";
string b = a;
fixed (char* p = a)
{
p[0] = 'B';
}
Console.WriteLine(a); // output: "Best"
Console.WriteLine(b); // output: "Best"
}
But note that this change only works in an unsafe block! because Strings are immutable (From MSDN):
The contents of a string object cannot be changed after the object is
created, although the syntax makes it appear as if you can do this.
For example, when you write this code, the compiler actually creates a
new string object to hold the new sequence of characters, and that new
object is assigned to b. The string "h" is then eligible for garbage
collection.
string b = "h";
b += "ello";
And keep in mind that:
Although the string is a reference type, the equality operators (== and
!=) are defined to compare the values of string objects, not
references.
Try:
public static void TestI(ref string test)
{
test = "after passing";
}
I believe your code is analogous to the following, and you should not have expected the value to have changed for the same reason it wouldn't here:
public static void Main()
{
StringWrapper testVariable = new StringWrapper("before passing");
Console.WriteLine(testVariable);
TestI(testVariable);
Console.WriteLine(testVariable);
}
public static void TestI(StringWrapper testParameter)
{
testParameter = new StringWrapper("after passing");
// this will change the object that testParameter is pointing/referring
// to but it doesn't change testVariable unless you use a reference
// parameter as indicated in other answers
}
Another way to bypass the string behavior. Use string array of ONE element only and manipulate this element.
class Test
{
public static void Main()
{
string[] test = new string[1] {"before passing"};
Console.WriteLine(ref test);
TestI(test);
Console.WriteLine(ref test);
}
public static void TestI(ref string[] test)
{
test[0] = "after passing";
}
}
From C programming language as far as I remember if any pointer is assigned to another pointer, we would use ** prefix for accessing to original value.
How does this work when I pass an object instance to a method as ref Myobject myObject argument. Does c# resolve the heap address of the object and call printSomething in such code below?
void testFunction(ref MyObject myObject){
myObject.printSomething();
}
So if this was a recursive function would compiler follow the reference addresses until it finds the object?
I am adding a test scenario below;
public class Test
{
public Test()
{
StringBuilder str = new StringBuilder();
Function(ref str);
Console.WriteLine(str.ToString());
}
int referenceCount = 0;
int Function(ref StringBuilder sBuilder)
{
referenceCount++;
if (referenceCount == 100)
{
sBuilder.Append("foo");
return referenceCount;
}
Function(ref sBuilder);
return referenceCount;
}
public static void Main(string[] args)
{
new Test();
Console.ReadLine();
}
}
If I remove if block as expected gives stack over flow exception so for each method call a new reference is reserved in stack.After linking 100 of them to each other I am calling a method on the last one.
Will sBuilder.Append("foo"); code follow the linked references until it reaches to the object?
Look at this article.
Ref in c# isn't pointer in c++. This is different concepts.
When used in a method's parameter list, the ref keyword indicates that
an argument is passed by reference, not by value. The effect of
passing by reference is that any change to the argument in the called
method is reflected in the calling method. 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.
This below code compiles and works out as intended.
class MyClass1
{
public void test()
{
string one = "testString1";
Console.WriteLine("MyClass1: " + one);
new MyClass2().test(one);
Console.WriteLine(one); //again testString1 is printed.
}
}
class MyClass2
{
public void test(string two)
{
Console.WriteLine("Test method");
Console.WriteLine(two);
two = "pilot";
Console.WriteLine(two);
}
}
all I infer from this is:
The value assigned to the string in test method is local to that function and the changes will be reflected only if I use a ref or out.
The question is:
We all know that the string is a reference type (because it is of type, String)
So, for all the reference types : when passing around their objects, the changes should be reflected right ? (For ex, for the same example, if I pass around a object of a class, then any changes are reflected back right ?)
Why is this rule not followed here ?
Can any one point me in understanding what happens under the hood ?
Although strings are reference objects, they are also immutable. Since references are passed by value *, changes to variables representing the reference, are not reflected on the original.
To demonstrate the effect of passing reference objects, replace string with StringBuilder, and change the content inside the test method:
class MyClass1
{
public void test()
{
StringBuilder one = new StringBuilder("testString1");
Console.WriteLine("MyClass1: " + one);
new MyClass2().test(one);
Console.WriteLine(one); //testString1pilot is printed.
}
}
class MyClass2
{
public void test(StringBuilder two)
{
Console.WriteLine("Test method");
Console.WriteLine(two);
two.Append("pilot");
Console.WriteLine(two);
}
}
* Unless the method specifies a different mode of parameter passing, e.g. out or ref.
So, for all the reference types : when passing around their objects,
the changes should be reflected right ?
All reference types are passed by reference is not true.
all reference type or value types are passed by value by default.
if you want to pass any type as reference types you need to use ref or out keyword.
Note: String is a immutable type means Strings can not be changed.
That is the reason why you are not able to see the changes made in the called function.
You need to use StringBuilder to get back the changes.
JonSteek has explained about Parmeter passing well here
In your example, the fact that String is a reference type does not matter. The exact same thing would happen with any value type or even a mutable reference type (like a class).
This is because the parameter to a method normally acts like a local variable within the method. Changes made to the parameter are local to the method.
As you stated, the exception is when the parameter is ref or out.
You have to understand the difference between the string which is a reference type and the variable itself that points to that object.
two = "pilot";
When you do this, you are creating a new string object and telling variable two to now point to this new string. The variable one still points to the original string, which is a different object.
I always thought that a method parameter with a class type is passed as a reference parameter by default. Apparently that is not always the case. Consider these unit tests in C# (using MSTest).
[TestClass]
public class Sandbox
{
private class TestRefClass
{
public int TestInt { get; set; }
}
private void TestDefaultMethod(TestRefClass testClass)
{
testClass.TestInt = 1;
}
private void TestAssignmentMethod(TestRefClass testClass)
{
testClass = new TestRefClass() { TestInt = 1 };
}
private void TestAssignmentRefMethod(ref TestRefClass testClass)
{
testClass = new TestRefClass() { TestInt = 1 };
}
[TestMethod]
public void DefaultTest()
{
var testObj = new TestRefClass() { TestInt = 0 };
TestDefaultMethod(testObj);
Assert.IsTrue(testObj.TestInt == 1);
}
[TestMethod]
public void AssignmentTest()
{
var testObj = new TestRefClass() { TestInt = 0 };
TestAssignmentMethod(testObj);
Assert.IsTrue(testObj.TestInt == 1);
}
[TestMethod]
public void AssignmentRefTest()
{
var testObj = new TestRefClass() { TestInt = 0 };
TestAssignmentRefMethod(ref testObj);
Assert.IsTrue(testObj.TestInt == 1);
}
}
The results are that AssignmentTest() fails and the other two test methods pass. I assume the issue is that assigning a new instance to the testClass parameter breaks the parameter reference, but somehow explicitly adding the ref keyword fixes this.
Can anyone give a good, detailed explanation of whats going on here? I'm mainly just trying to expand my knowledge of C#; I don't have any specific scenario I'm trying to solve...
The thing that is nearly always forgotten is that a class isn't passed by reference, the reference to the class is passed by value.
This is important. Instead of copying the entire class (pass by value in the stereotypical sense), the reference to that class (I'm trying to avoid saying "pointer") is copied. This is 4 or 8 bytes; much more palatable than copying the whole class and in effect means the class is passed "by reference".
At this point, the method has it's own copy of the reference to the class. Assignment to that reference is scoped within the method (the method re-assigned only its own copy of the reference).
Dereferencing that reference (as in, talking to class members) would work as you'd expect: you'd see the underlying class unless you change it to look at a new instance (which is what you do in your failing test).
Using the ref keyword is effectively passing the reference itself by reference (pointer to a pointer sort of thing).
As always, Jon Skeet has provided a very well written overview:
http://www.yoda.arachsys.com/csharp/parameters.html
Pay attention to the "Reference parameters" part:
Reference parameters don't pass the values of the variables used in
the function member invocation - they use the variables themselves.
If the method assigns something to a ref reference, then the caller's copy is also affected (as you have observed) because they are looking at the same reference to an instance in memory (as opposed to each having their own copy).
The default convention for parameters in C# is pass by value. This is true whether the parameter is a class or struct. In the class case just the reference is passed by value while in the struct case a shallow copy of the entire object is passed.
When you enter the TestAssignmentMethod there are 2 references to a single object: testObj which lives in AssignmentTest and testClass which lives in TestAssignmentMethod. If you were to mutate the actual object via testClass or testObj it would be visible to both references since they both point to the same object. In the first line though you execute
testClass = new TestRefClass() { TestInt = 1 }
This creates a new object and points testClass to it. This doesn't alter where the testObj reference points in any way because testClass is an independent copy. There are now 2 objects and 2 references which each reference pointing to a different object instance.
If you want pass by reference semantics you need to use a ref parameter.
My 2 cents
When a class is passed to a method, a copy of its memory space address is being sent (a direction to your house is being sent). So any operation on that address will affect the house but will not change the address itself. (This is default).
Passing a class (object) by reference has an effect of passing its actual address instead of a copy of an address. That means if you assign a new object to an argument passed by reference it will change the actual address (similar to relocation). :D
This is how I see it.
The AssignmentTest uses TestAssignmentMethod which only changes the object reference passed by value.
So the object itself is passed by reference but the reference to the object is passed by value. so when you do:
testClass = new TestRefClass() { TestInt = 1 };
You are changing the local copied reference passed to the method not the reference you have in the test.
So here:
[TestMethod]
public void AssignmentTest()
{
var testObj = new TestRefClass() { TestInt = 0 };
TestAssignmentMethod(testObj);
Assert.IsTrue(testObj.TestInt == 1);
}
testObj is a reference variable. When you pass it to TestAssignmentMethod(testObj);, the refernce is passed by value. so when you change it in the method, original reference still points to the same object.
There are lot's of subtleties missed in the posted answers here that will create unexpected results and confuse new C# developers. There are actually two ways to process a reference passed by value in C# methods.
All methods in C# pass arguments in BY VALUE by default unless you use the ref, in, or out keywords. Passing a REFERENCE BY VALUE means a COPY of the MEMORY ADDRESS of the object used by the outside reference is passed in and assigned to the method parameter. The original outside variable address is not passed in nor the original object in memory, just the memory address to the object.
Both variables now point to the same object in memory.
This copy of the address to the object in memory is the VALUE for pass by value for all reference types. That means the original reference variable that points to the object address remains the same, and a new copy of that memory address is assigned to a new variable in the method parameter. They BOTH point to the same object. That means if either change properties on the object, it will affect the original object and will be seen by both variables.
This seems to act like a PASS BY REFERENCE, but it is not. That is what confuses many developers.
But this means some "weird" and unexpected things can happen passing a reference by value in methods if you are not careful. It means your method variable can connect to the same object and change the properties and fields of the original shared object ...BUT... as soon as you reassign the method variable to a new instance of the same type of object, it loses a connection to the original instance and no longer affects the original object used by the outside reference.
You might assume the method has assigned a fresh object to the outside reference variable, but you have not! Changing that new object's properties in the method no longer affect the outside reference. So BE CAREFUL!
Let's test this weirdness in C#:
// First, create my cat class. I can change its name
// to anything I want. But instead, I want it to have
// a special name assigned by the next class via a method.
class MyCat
{
public string Name { get; set; }
}
// This special class will assign a popular name to me cat.
class CatNames
{
public enum PopularNames {
Felix,
Fluffy
}
public void ChangeName(MyCat c)
{
PopularNames p = PopularNames.Felix;
c.Name = p.ToString();
}
public void ChangeNameAndCat(MyCat c)
{
PopularNames p = PopularNames.Fluffy;
MyCat d = new MyCat();
d.Name = p.ToString();
c = d;
// Note: In this case, you might want to return the new "MyCat"
// object and its name to the caller.
}
}
// Testing passing by value and how references are passed...
CatNames catnamechanger = new CatNames();
// I created two cats with the same name so you can see
// what names actually changed below.
MyCat cat1 = new MyCat();
cat1.Name = "Bubba";
MyCat cat2 = new MyCat();
cat2.Name = "Bubba";
catnamechanger.ChangeName(cat1);
catnamechanger.ChangeNameAndCat(cat2);
Console.WriteLine("My Cat1's Name is: " + cat1.Name);
Console.WriteLine("My Cat2's Name is: " + cat2.Name);
// ============== OUTPUT ==================
// My Cat1's Name is: Felix
// My Cat2's Name is: Bubba <<< OOPS! My cat name kept the original
RESULTS
Notice the first cat had its name changed on the original object, but the second cat kept its original name, "Bubba", as a new cat was assigned to the method variable. It lost connection to the original object. The reason is, passing a reference by value still allows you to affect properties of the passed in address to the original object. But as soon as you change where the method variable points, that reference is lost.
In c# , when sending a parameter to a method, when should we use "ref" and when "out" and when without any of them?
In general, you should avoid using ref and out, if possible.
That being said, use ref when the method might need to modify the value. Use out when the method always should assign something to the value.
The difference between ref and out, is that when using out, the compiler enforces the rule, that you need to assign something to the out paramter before returning. When using ref, you must assign a value to the variable before using it as a ref parameter.
Obviously, the above applies, when you are writing your own methods. If you need to call methods that was declared with the ref or out modifiers on their parameters, you should use the same modifier before your parameter, when calling the method.
Also remember, that C# passes reference types (classes) by reference (as in, the reference is passed by value). So if you provide some method with a reference type as a parameter, the method can modify the data of the object; even without ref or out. But it cannot modify the reference itself (as in, it cannot modify which object is being referenced).
They are used mainly to obtain multiple return values from a method call. Personally, I tend to not use them. If I want multiple return values from a method then I'll create a small class to hold them.
ref and out are used when you want something back from the method in that parameter. As I recall, they both actually compile down to the same IL, but C# puts in place some extra stuff so you have to be specific.
Here are some examples:
static void Main(string[] args)
{
string myString;
MyMethod0(myString);
Console.WriteLine(myString);
Console.ReadLine();
}
public static void MyMethod0(string param1)
{
param1 = "Hello";
}
The above won't compile because myString is never initialised. If myString is initialised to string.Empty then the output of the program will be a empty line because all MyMethod0 does is assign a new string to a local reference to param1.
static void Main(string[] args)
{
string myString;
MyMethod1(out myString);
Console.WriteLine(myString);
Console.ReadLine();
}
public static void MyMethod1(out string param1)
{
param1 = "Hello";
}
myString is not initialised in the Main method, yet, the program outputs "Hello". This is because the myString reference in the Main method is being updated from MyMethod1. MyMethod1 does not expect param1 to already contain anything, so it can be left uninitialised. However, the method should be assigning something.
static void Main(string[] args)
{
string myString;
MyMethod2(ref myString);
Console.WriteLine(myString);
Console.ReadLine();
}
public static void MyMethod2(ref string param1)
{
param1 = "Hello";
}
This, again, will not compile. This is because ref demands that myString in the Main method is initialised to something first. But, if the Main method is changed so that myString is initialised to string.Empty then the code will compile and the output will be Hello.
So, the difference is out can be used with an uninitialised object, ref must be passed an initialised object. And if you pass an object without either the reference to it cannot be replaced.
Just to be clear: If the object being passed is a reference type already then the method can update the object and the updates are reflected in the calling code, however the reference to the object cannot be changed. So if I write code like this:
static void Main(string[] args)
{
string myString = "Hello";
MyMethod0(myString);
Console.WriteLine(myString);
Console.ReadLine();
}
public static void MyMethod0(string param1)
{
param1 = "World";
}
The output from the program will be Hello, and not World because the method only changed its local copy of the reference, not the reference that was passed in.
I hope this makes sense. My general rule of thumb is simply not to use them. I feel it is a throw back to pre-OO days. (But, that's just my opinion)
(this is supplemental to the existing answers - a few extra considerations)
There is another scenario for using ref with C#, more commonly seen in things like XNA... Normally, when you pass a value-type (struct) around, it gets cloned. This uses stack-space and a few CPU cycles, and has the side-effect that any modifications to the struct in the invoked method are lost.
(aside: normally structs should be immutable, but mutable structs isn't uncommon in XNA)
To get around this, it is quite common to see ref in such programs.
But in most programs (i.e. where you are using classes as the default), you can normally just pass the reference "by value" (i.e. no ref/out).
Another very common use-case of out is the Try* pattern, for example:
string s = Console.ReadLine();
int i;
if(int.TryParse(s, out i)) {
Console.WriteLine("You entered a valid int: " + i);
}
Or similarly, TryGetValue on a dictionary.
This could use a tuple instead, but it is such a common pattern that it is reasonably understood, even by people who struggle with too much ref/out.
Very simple really. You use exactly the same keyword that the parameter was originally declared with in the method. If it was declared as out, you have to use out. If it was declared as ref, you have to use ref.
In addition to Colin's detailed answer, you could also use out parameters to return multiple values from one method call. See for example the method below which returns 3 values.
static void AssignSomeValues(out int first, out bool second, out string third)
{
first = 12 + 12;
second = false;
third = "Output parameters are okay";
}
You could use it like so
static void Main(string[] args) {
int i;
string s;
bool b;
AssignSomeValues(out i, out b, out s);
Console.WriteLine("Int value: {0}", i);
Console.WriteLine("Bool value: {0}", b);
Console.WriteLine("String value: {0}", s);
//wait for enter key to terminate program
Console.ReadLine(); }
Just make sure that you assign a valid value to each out parameter to avoid getting an error.
Try to avoid using ref. Out is okay, because you know what will happen, the old value will be gone and a new value will be in your variable even if the function failed. However, just by looking at the function you have no idea what will happen to a ref parameter. It may be the same, modified, or an entirely new object.
Whenever I see ref, I get nervous.
ref is to be avoided (I beleive there is an fx-cop rule for this also) however use ref when the object that is reference may itself changed. If you see the 'ref' keyword you know that the underlying object may no longer be referenced by the same variable after the method is called.