methods change variable out of its scope - c#

In this small piece of code, changing a 2 dimensional array inside a method leads to changes the variable in main method.
What is the cause of this and how can I protect the variable in main method to remain unchanged?
using System;
namespace example
{
class Program
{
static void Main(string[] args)
{
string[,] string_variable = new string[1, 1];
string_variable[0, 0] = "unchanged";
Console.Write("Before calling the method string variable is {0}.\n", string_variable[0,0]);
Function(string_variable);
Console.Write("Why after calling the method string variable is {0}? I want it remain unchanged.\n", string_variable[0, 0]);
Console.ReadKey();
}
private static void Function(string [,] method_var)
{
method_var[0, 0] ="changed";
Console.Write("Inside the method string variable is {0}.\n", method_var[0, 0]);
}
}
}
At the end this is the program output:
Before calling the method string variable is unchanged.
Inside the method string variable is changed.
Why after calling the method string variable is changed? I want it remain unchanged.
EDIT 1: A question come in my mind is : What are other common programming languages that doesn't have this sort of problem?
EDIT 2: For sack of comparison, I write this somehow identical code with string variable instead of array and the output is as expected and is just fine:
using System;
namespace example
{
class Program
{
static void Main(string[] args)
{
string string_variable;
string_variable= "unchanged";
Console.Write("Before calling the method string variable is {0}.\n", string_variable);
Function(string_variable);
Console.Write("after calling the method string variable is {0} as expected.\n", string_variable);
Console.ReadKey();
}
private static void Function(string method_var)
{
method_var ="changed";
Console.Write("Inside the method string variable is {0}.\n", method_var);
}
}
}
and the output of this code is :
Before calling the method string variable is unchanged.
Inside the method string variable is changed.
after calling the method string variable is unchanged as expected.
Last EDIT : Thanks everybody for clarification, Hope this will become useful for others in future.

When you pass an array to a method (or any reference type), you're passing a reference to the memory where that array exists. So making a change to the array will behave exactly the same as if you'd made that change in the method in which the array was originally instantiated.
Unfortunately, there's no way to make an array read-only in C#. However, you could create a wrapper class as described here that provides a mechanism for accessing the values in the array, without providing a way to change the array. Then you could pass that wrapper class into Function instead of passing the array itself.
public class ReadOnlyTwoDimensionalArray<T>
{
private T[,] _arr;
public ReadOnlyTwoDimensionalArray(T[,] arr)
{
_arr = arr;
}
public T this[int index1, int index2]
{
get {return _arr[index1, index2];}
}
}
Then:
static void Main(string[] args)
{
string[,] string_variable = new string[1, 1];
string_variable[0, 0] = "unchanged";
Console.Write("Before calling the method string variable is {0}.\n", string_variable[0,0]);
Function(new ReadOnlyTwoDimensionalArray<string>(string_variable));
Console.Write("Can't touch this: {0}.\n", string_variable[0, 0]);
Console.ReadKey();
}
private static void Function(ReadOnlyTwoDimensionalArray<string> method_var)
{
// the compiler will complain if you try to do this:
//method_var[0, 0] ="changed";
// but this works just fine
Console.Write("Inside the method string variable is {0}.\n", method_var[0, 0]);
}
Alternatively, you could make sure that you only give Function a copy of the original array. But that obviously has some performance implications.
Response to Edits
The example you give to show how string variables work isn't really equivalent to the original. In that second example, you are changing the value of the variable locally, but that value is just a memory address--you're not actually changing the string itself. You could do the same thing with an array like this:
private static void Function(string [,] method_var)
{
method_var = new string[1, 1] {{"changed"}};
Console.Write("Inside the method string variable is {0}.\n", method_var[0, 0]);
}
By doing this, you are changing the value of the method_var variable, not the values in the array that it is pointing to.
Eric Lippert's comments below this post explain very clearly how C/C++ can give you read-only behavior on an array, but won't allow you to change the array's values locally without also changing them in the array that the calling method is referencing. He rightly points out that this is not a limitation of C#: it is a fundamental principle of how memory allocation works. Values passed from one method to another can either be passed by copying all their contents or by just copying a reference to their contents. For small values, you can have a value type, and C# will pass their entire value. For larger values like arrays, it would be expensive and error-prone to copy their entire value, so the language will not attempt to do this automatically--you must do it explicitly.

I'd just simply use Copy() to get a new instance with the same content:
string[,] newArray = new string[1, 1];
Array.Copy(string_variable , 0, newArray, 0, string_variable.Lenght);
Function(newArray);

Related

Wierd way C# passes in lists as arguments

So i ran into this bug where C# behaves like i passed in a list by reference and not by value, like it usually does. Let me show an example:
using System;
using System.Collections.Generic;
namespace testprogram
{
class Program
{
static int x;
static List<Coordinate> coordinates;
static void Main(string[] args)
{
x = 10;
coordinates = new List<Coordinate>();
coordinates.Add(new Coordinate(0, 0));
coordinates.Add(new Coordinate(1, 1));
testfunction(x, coordinates);
Console.WriteLine(x);
foreach (var objekt in coordinates)
{
Console.WriteLine(objekt.xpos);
Console.WriteLine(objekt.ypos);
}
Console.Read();
}
static void testfunction(int test, List<Coordinate> objects)
{
test = 4;
foreach (Coordinate obj in objects)
{
obj.xpos = 4;
obj.ypos = 4;
}
}
}
class Coordinate
{
public int xpos;
public int ypos;
public Coordinate(int new_x, int new_y)
{
xpos = new_x;
ypos = new_y;
}
}
}
This code outputs:
10
4
4
4
4
But why? I expected it to be:
10
0
0
1
1
I tried to make an extra List in the Funktion and assign the value of the parameter to it but even that didn´t work. Is there some workaround for this?
The List<T> argument is a reference type which is passed by value.
If you modify any items in the list within the method, these will remain changed when you leave the method.
However, if you reassign the reference inside the method, these changes will be lost outside the method. If you want to pass a parameter by reference, you use the ref keyword:
static void testfunction(int test, ref List<Coordinate> objects)
{
// This will update objects outside the testfunction method
objects = new List<Coordinate>();
}
You can also use the out keyword, which works similar to the ref keyword. The only difference is that you don't have to initialise values that are passed in as out parameters before you call the method, and they must be initialised before leaving the method.
static void Main(string[] args)
{
// No need to initialise variable passed as "out" parameter
List<Coordinate> objects;
testfunction(test, out objects);
}
static void testfunction(int test, out List<Coordinate> objects)
{
// Removing this line would result in a compilation error
objects = new List<Coordinate>();
}
Your expectations are wrong. The list is passed as a value. It means that variable named objects is a value-copy of original list variable. But the list contains references, I mean the contents of the list are references to Coordinate objects. So if you would try to change the list variable like objects = new List<>(), it wouldn't change your original list. But if you change the objects inside the list, the changes are actually applied to the original list.
objects[0] = new Coordinate(5, 5);
objects[0].xpos = 6;
Both of these examples change the original list.
If you want to have the possibility to safely change anything in the list, you have to make a deep clone of it. You can use Clone() method, but it can be tricky, because you must manually clone each object inside the list. This was already answered here:
How create a new deep copy (clone) of a List<T>?
You passed a reference to the list "by value". So, if you change the reference "object" in "testfunction", then "coordinates" will not change (as a pointer). But changing the elements of "object" will affect the elements of "coordinates".

Type 'string' as an argument in C# function

The string type in C# is a reference type, and passing a reference type argument by value copies the reference so that I don't need to use the ref modifier. However, I need to use the ref modifier for modifying the input string. Why is this?
using System;
class TestIt
{
static void Function(ref string input)
{
input = "modified";
}
static void Function2(int[] val) // Don't need ref for reference type
{
val[0] = 100;
}
static void Main()
{
string input = "original";
Console.WriteLine(input);
Function(ref input); // Need ref to modify the input
Console.WriteLine(input);
int[] val = new int[10];
val[0] = 1;
Function2(val);
Console.WriteLine(val[0]);
}
}
The reason you need to ref the string parameter is that even though you pass in a reference to a string object, assigning something else to the parameter will just replace the reference currently stored in the parameter variable. In other words, you have changed what the parameter refers to, but the original object is unchanged.
When you ref the parameter, you have told the function that the parameter is actually an alias for the passed-in variable, so assigning to it will have the desired effect.
EDIT: Note that while string is an immutable reference type, that's not too relevant here. Since you're just trying to assign a new object (in this case the string object "modified"), your approach wouldn't work with any reference type. For example, consider this slight modification to your code:
using System;
class TestIt
{
static void Function(ref string input)
{
input = "modified";
}
static void Function2(int[] val) // don't need ref for reference type
{
val = new int[10]; // Change: create and assign a new array to the parameter variable
val[0] = 100;
}
static void Main()
{
string input = "original";
Console.WriteLine(input);
Function(ref input); // need ref to modify the input
Console.WriteLine(input);
int[] val = new int[10];
val[0] = 1;
Function2(val);
Console.WriteLine(val[0]); // This line still prints 1, not 100!
}
}
Now, the array test "fails", because you're assigning a new object to the non-ref parameter variable.
It helps to compare string to a type that is like string but is mutable. Let's see a short example with StringBuilder:
public void Caller1()
{
var builder = new StringBuilder("input");
Console.WriteLine("Before: {0}", builder.ToString());
ChangeBuilder(builder);
Console.WriteLine("After: {0}", builder.ToString());
}
public void ChangeBuilder(StringBuilder builder)
{
builder.Clear();
builder.Append("output");
}
This produces:
Before: input
After: output
So we see that for a mutable type, i.e. a type that can have its value modified, it is possible to pass a reference to that type to a method like ChangeBuilder and not use ref or out and still have the value changed after we called it.
And notice that at no time did we actually set builder to a different value in ChangeBuilder.
By contrast, if we do the same thing with string:
public void Caller2()
{
var s = "input";
Console.WriteLine("Before: {0}", s);
TryToChangeString(s);
Console.WriteLine("After: {0}", s);
}
public void TryToChangeString(string s)
{
s = "output";
}
This produces:
Before: input
After: input
Why? Because in TryToChangeString we are not actually changing the contents of the string referenced by the variable s, we are replacing s with an entirely new string. Furthermore, s is a local variable to TryToChangeString and so replacing the value of s inside the function has no effect on the variable that was passed in to the function call.
Because a string is immutable, there is no way, without using ref or out, to affect the callers string.
Finally, the last example does what we want with string:
public void Caller3()
{
var s = "input";
Console.WriteLine("Before: {0}", s);
ChangeString(ref s);
Console.WriteLine("After: {0}", s);
}
public void ChangeString(ref string s)
{
s = "output";
}
This produces:
Before: input
After: output
The ref parameter actually makes the two s variables aliases for each other. It's as though they were the same variable.
Strings are immutable - you are not modifying the string but replacing the object the reference points to with another one.
Compare that with e.g., a List: To add Items, you don't need ref. To replace the entire list with a different object, you need ref (or out).
This is the case for all immutable types. string happens to be immutable.
In order to change the immutable type outside of the method, you must change the reference. Therefore either ref or out is required to have an effect outside of the method.
Note: It's worth noting that in your example, you are calling out a particular case that does not match the other example: you are actually pointing to a different reference rather than simply changing the existing reference. As noted by dlev (and the Skeet himself in my comments), if you did the same for all other types (e.g., val = new int[1]), including mutable ones, then you will "lose" your changes once the method returns because they did not happen to the same object in memory, unless you use ref or out like you did with string above.
To hopefully clarify:
You are passing in a pointer that points to your object in memory. Without ref or out, a new pointer is made that points to the exact same location, and all changes happen using the copied pointer. Using them, the same pointer is used and all changes made to the pointer are reflected outside the method.
If your object is mutable, then that means that it can be changed without creating a new instance of the object. If you create a new instance, then you must point to somewhere else in memory, which means you must change your pointer.
Now, if your object is immutable, then that means that it cannot be changed without creating a new instance.
In your example, you created a new instance of a string (equal to "modified") and then changed the pointer (input) to point to that new instance. For the int array, you changed one of the 10 values effectively pointed to by val, which does not require messing with val's pointer--it simply goes to where you want (the first element of the array), and then modifies that first value, in-place.
A more similar example would be (stolen from dlev, but this is how to make them truly comparable):
static void Function(ref string input)
{
input = "modified";
}
static void Function2(int[] val)
{
val = new int[1];
val[0] = 100;
}
Both functions change their parameter's pointer. Only because you used ref does input "remember" its changes, because when it changes the pointer, it is changing the pointer that was passed in and not just a copy of it.
val will still be an array of 10 ints outside of the function, and val[0] will still be 1 because the "val" within Function2 is a different pointer that originally points to the same location as Main's val, but it points somewhere else after the new array is created (the different pointer points to the new array, and the original pointer continues to point to the same location).
If I used ref with the int array, then it to would have changed. And it would have changed in size too.
A better example for newbies:
string a = "one";
string b = a;
string b = "two";
Console.WriteLine(a);
... will output "one".
Why? Because you are assigning a whole new string into pointer b.
The confusion is that ref type references are passed by value by default, to modify the reference itself (what the object points to) you have to pass the reference by reference - using ref.
In your case you are handling strings - assigning a string to a variable (or appending to, etc.) changes the reference, since strings are immutable there is no way to avoid this either, so you have to use ref.
You're right. Arrays and strings are reference types. But if to be honest and compare similar behavior you should write something like this:
static void Function2(int[] val) // It doesn't need 'ref' for a reference type
{
val = new[] { 1, 2, 3, 4 };
}
But in your example you perform a write operation in some element of a C# one-dimensional array via reference val.

Changing reference types(strings) inside methods

I am passing a string variable to a method. I know strings are reference types but the value that I assign inside the method is lost.
public static void TestMethod(string myString)
{
myString = "world";
}
static void Main(string[] args)
{
string s = "hello";
Console.WriteLine(s); // output is "hello"
TestMethod(s);
Console.WriteLine(s); // output is also "hello" not "world" !?
}
Anyway this does not happen with an array for example. Can someone explain why might be the cause?
Because myString = "world" assigns a new string to the parameter, not updating the existing string. To update the original reference to the string you must pass the parameter with ref.
public static void TestMethod(ref string myString)
{
myString = "world";
}
static void Main(string[] args)
{
string s = "hello";
Console.WriteLine(s); // output is "hello"
TestMethod(ref s);
Console.WriteLine(s); // output is also "hello" not "world" !?
}
Yes, because without a ref (or out) you can't assign a new object to a parameter. Since you didn't pass it in through a ref, the variable outside the method still references the original string which didn't change. Consequently, strings are immutable, so you can't do anything to it without create a new string once it is instantiated.
The array can be altered (or the contents of the array can be altered), because the references inside the array are not immutable (you can say reassign my_object1 to equal "BLAH"). You can replace a value in the array and have it accessible outside of the array, because the reference to the array outside of the method hasn't changed.
Link to String in MSDN (talks about immutability)
For this to work you need to add the "ref" keyword to the parameter in the method signature.
Though your string is passed by reference, when you pass it to your method you have 2 references to the same string - the one in the Main() and the one in TestMethod(). When you assign a new value to the variable in the TestMethod() you are changing its reference, but not what Main()'s variable is referencing.
If you were able to just change the string from the TestMethod() instead of reassigning then you would see the effects in the Main(), but you can't with strings since they are immutable.
To play with this further you can try the following - change TestMethod() to receive a IList and add items to this list. You can see these new items in the variable you passed in from Main(). Now if you change TestMethod(IList listArg) to first reassign listArg to a new list (i.e. listArg = new List) and then add items, the list in Main() remains unchanged. This is the same idea.
Strings are immutable, which means that you can't change it's value, like you do with the items in an array. You can only replace a string object with a different string object.
The same happens if you try to replace an array object. This will put a new array in the parameter variable, but it will not change the variable that you used to call the method, and thus does not replace the array that you passed in:
public static void TestMethod(string[] myArray) {
myArray = new string[] { "world" };
}
Parameters are always passed by value unless you use the ref or out keywords. For a reference type that means that you pass a copy of the reference.
Using the ref keyword you pass the variable, so that you can change it in the method:
public static void TestMethod(ref string myString) {
myString = "world";
}

Why Can I Change Struct's int[] Property from Method Without Specifying "ref"?

From a method, I can pass a struct which contains an array of integers, and change the values in the array. I am not sure I understand fully why I can do this. Can someone please explain why I can change the values stored in the int[]?
private void DoIt(){
SearchInfo a = new SearchInfo();
a.Index = 1;
a.Map = new int[] { 1 };
SearchInfo b = new SearchInfo();
b.Index = 1;
b.Map = new int[] { 1 };
ModifyA(a);
ModifyB(ref b);
Debug.Assert(a.Index == 1);
Debug.Assert(a.Map[0] == 1, "why did this change?");
Debug.Assert(b.Index == 99);
Debug.Assert(b.Map[0] == 99);
}
void ModifyA(SearchInfo a) {
a.Index = 99;
a.Map[0] = 99;
}
void ModifyB(ref SearchInfo b) {
b.Index = 99;
b.Map[0] = 99;
}
struct SearchInfo {
public int[] Map;
public int Index;
}
In C#, references are passed by value. An array is not copied when passed to method or when stored in an instance of another class. - a reference to the array is passed. This means a method which recieves a reference to an array (either directly or as part of another object) can modify the elements of that array.
Unlike languages like C++, you cannot declare "immutable" arrays in C# - you can however uses classes like List which have readonly wrappers available to prevent modification to the collection.
From a method, I can pass a struct which contains an array of integers, and change the values in the array. I am not sure I understand fully why I can do this.
An array is defined as a collection of variables.
Variables, by definition, can be changed. That is why we call them "variables".
Therefore when you pass an array, you can change the contents; the contents of an array are variables.
Why can I change a struct’s int[] property without specifying “ref”?
Remember, as we discussed before in a different question, you use ref to make an alias to a variable. That is what "ref" is for -- making aliases to variables. (It is unfortunate that the keyword is the confusing "ref" -- it probably would have been more clear to make it "alias".)
From MSDN:
Do not return an internal instance of an array. This allows calling code to change the array. The following example demonstrates how the array badChars can be changed by any code that accesses the Path property even though the property does not implement the set accessor.
using System;
using System.Collections;
public class ExampleClass
{
public sealed class Path
{
private Path(){}
private static char[] badChars = {'\"', '<', '>'};
public static char[] GetInvalidPathChars()
{
return badChars;
}
}
public static void Main()
{
// The following code displays the elements of the
// array as expected.
foreach(char c in Path.GetInvalidPathChars())
{
Console.Write(c);
}
Console.WriteLine();
// The following code sets all the values to A.
Path.GetInvalidPathChars()[0] = 'A';
Path.GetInvalidPathChars()[1] = 'A';
Path.GetInvalidPathChars()[2] = 'A';
// The following code displays the elements of the array to the
// console. Note that the values have changed.
foreach(char c in Path.GetInvalidPathChars())
{
Console.Write(c);
}
}
}
You cannot correct the problem in the preceding example by making the badChars array readonly (ReadOnly in Visual Basic). You can clone the badChars array and return the copy, but this has significant performance implications.
Although your SearchInfo struct is a value type, the .Map field is holding a reference, because Array is a reference type. Think of this reference as the address pointing to the memory location where the array resides.
When you pass an instance of SearchInfo to a method, as you know, the SearchInfo gets copied. And the copy naturally contains the very same address pointing to the very same array.
In other words, copying the struct doesn't make a copy of the array, it just makes a copy of the pointer.
Well, it is passed by reference anyway, like all reference types in C#.
Neither C# nor CLR support constness, unfortunately, so the platform doesn't really know if you are allowed to change it or not. So, it has the reference, it may use it to change the value, and there's nothing to stop it from doing so.
You may see it as a language design bug, btw. It is unexpected for the user.

Handling Reference Types

When reference variable can be passed by reference :
class Example
{
public string str="Demo";
public int[] intValues={1,3,4,5};
public static void StrPassing(string someStr)
{
string otherStr="Changed!";
someStr=otherStr;
}
public static void NumPassing(int[] a)
{
a[2] = 115;
}
}
static void Main(string[] args)
{
Example ex = new Example();
Example.StrPassing(ex.str);
Example.NumPassing(ex.intValues);
foreach (int i in ex.intValues)
{
Console.WriteLine(i);
}
Console.WriteLine(ex.str);
Console.ReadLine();
}
the value of intValues[2] is changed as 115 as the reference is being passed.But the value of the string "str" (demo) is not changed to "Changed!".What is the reason for it?.can i take it as Arrays are passed by reference and other reference types are passed by
value?
Whatever you pass to a method as arguments is passed by value which, for reference types, means that a reference is passed by value. So you can't change the object to another one but you can surely change its contents (because that doesn't change the actual reference, just some memory elsewhere).
As your example with the array demonstrates you take the array reference (but don't change it) and change a value in the array. This is just like taking some object and changing a property value. You can do this just fine from within a method too.
If you want to change a string, which is an immutable object in .NET, then you need to resort to ref parameters:
public static void StrPassing(ref string someStr)
{
string otherStr="Changed!";
someStr=otherStr;
}
And call it like this:
string foo = "foo";
StrPassing(ref foo);
Console.WriteLine(foo); // should print "Changed!"
The ref keyword ensures that your method gets the actual reference to the string and can change it, instead of just a copy of the reference. So then you can replace the object by an entirely new one.
To come back to your array: You'd have a hard time too, to change the passed array to an entirely different array:
public static void NumPassing(int[] a)
{
a = new int[15];
}
wouldn't work too because then you'd try exactly the same as changing a string to an entirely different string.
You need to distinguish between changing which object a variable refers to and changing *the content of the object".
In this code:
public static void StrPassing(string someStr)
{
string otherStr="Changed!";
someStr=otherStr;
}
... you are changing the value of someStr. You're not making any change to the string that someStr originally refers to. Indeed, you can't because strings are immutable. (If it were a StringBuilder, you could set the length to 0 and then append "Changed!")
Changing the value of someStr has no effect because the argument (ex.str) was passed by value. (The value in question is a reference, but that doesn't mean it's passed by reference.)
Now compare that with this code:
public static void NumPassing(int[] a)
{
a[2] = 115;
}
Here you're not changing the value of a - you're changing the contents of the array that a refers to.
In short, unless you use ref/out, arguments will be passed by value - but for reference types that value is just a reference.
I have an article on parameter passing which goes into all of this in a lot more detail.
What you'd need to do is change the signature for StrPassing to look like this:
public static void StrPassing(ref string someStr)
Strings are special in C#. They are immutable reference types which makes they exhibit similar behavior as value types.
Here's a good discussion.

Categories