Returning array from methods C# - c#

I have been wondering, why isn't my method, modifying the array, when I used it as a parameter and made it equal to another array with different values, inside the method?
Am I just changing the reference address?
static void Main()
{
string[] array = { "yes", "no", "maybe" };
TestO(array); // Still "yes", "no", "maybe"
}
static void TestO(string[] array)
{
string[] secondArray = new string[array.Length];
secondArray[0] = "1";
secondArray[1] = "2";
secondArray[2] = "3";
array = secondArray;
}
My guess: I did not modify the array inside the Main(), because when doing array = secondArray; in the Test0() method,
I just changed the reference address of array to secondArray.
If my guess is not right, my question is, why exactly is it not getting modified?
(I know that I can just modify Test0() to a string[] return method and return the modified secondArray and pass it on to the array in Main())
Another questions is:
If I use the string[] return method, and declare the following:
static void Main()
{
string[] array = { "yes", "no", "maybe" };
array = TestO(array);
}
static string[] TestO(string[] methodArray)
{
string[] secondArray = new string[methodArray.Length];
secondArray[0] = "1";
secondArray[1] = "2";
secondArray[2] = "3";
return secondArray;
}
array = TestO(array); am I just passing the reference address of the secondArray[] to array[] or am I passing only the values of it? (Most probably it will be the reference address, but I wanted to be sure if mistaken)

I just changed the reference address of array to secondArray. If my guess is not right, my question is, why exactly is it not getting modified?
I think you're leaning towards appreciating that there are two references to one data in memory (there are not two data); you have a variable array that points to data. You call a method and another, copy reference is established to the same data. You made a new object, then you pointed the copied reference at the new object, leaving the original reference pointing to the original data, then you threw the copy reference and the new data away.. You're back to the exact same situation you started with.
Pictorially, line by line, it might look like (I renamed your method argument to arrayX so the difference is obvious):
If you decorate the argument with ref and call it with ref there is no copy, so the called method can modify the original reference and point it somewhere else:
Note that in either of these cases it's perfectly possible to modify the contents of the array. Doing:
arrayX[0] = "New data";
..would take effect in either case and printing array[0] would show "New data". When modifying the data at the end of the arrow it matters not whether the starting point is an original reference or a copy. This is purely about whether the method has the power to point the original reference passed to it, to a different object or not
Generally we don't do it. We adopt the style of your second code block - to return the data. It may help to see it as rude - imagine your friend says he'll look after your plant while you're on holiday; you give your keys to your friend. He swaps your plant for a different one that he likes better; you're upset because you had that plant for ten years..
There are very few reasons to use ref, or the related "power to overwite your reference" - out. Don't use it for "I wanted to return multiple things from my method" - in an OO world we can always return one thing that represents two items of data. We don't need to "return two things"; we can return one thing with two things inside it...
//don't:
void GetPerson(ref string name, ref int age)
//consider a class:
Person GetPerson()
return new Person(){ Name = ..., Age = ...}
//or a Tuple
(string Name, int Age) GetPerson
Let the calling method choose whether its own variable should be overwritten, rather than having some "third party" pull that rug from under its feet

you don't need to return anything , just use a ref
static void Test( ref string[] array)
{
string[] secondArray = new string[array.Length];
secondArray[0] = "1";
secondArray[1] = "2";
secondArray[2] = "3";
array = secondArray;
}

When you pass an array to a method
static void Test0(string[] array)
{
You are passing a reference to that array. That reference is effectively immutable (you're getting a copy of the reference, not the original one), so you can't change the parameter reference and expect it to affect the code outside of the Test method.
While you can do this to get the behavior you want:
static void Test0(ref string[] array)
{
it's not considered good C# style. Strings are themselves immutable; the "correct" style is to return them from the method. This is also true of string arrays.
The only thing you would be saving by using this technique is an extra reference and a return statement, since you're still creating new strings and a new array anyway.

Related

How do you return an array in a procedure back to the main procedure in C#?

static string readfileName(string[] name)
{
using (StreamReader file = new StreamReader("StudentMarks.txt"))
{
int counter = 0;
string ln;
while ((ln = file.ReadLine()) != null)
{
if (ln.Length > 4)
{
name[counter] = ln;
counter++;
}
}
file.Close();
return name;
}
}
This is the procedure I'm currently trying to return the array name[50] but the compile time error I can't fix states
"Error CS0029 Cannot implicitly convert type 'string[]' to 'string' "
You don't need to. Your main method passed the array to this method, this method filled it. It doesn't need to hand it back because the object pointed to by your 'name` variable is the same object as pointed to by the original variable in the main method; your main method already has all the array data:
static void Main(){
var x = new string[10];
MyMethod(x);
Console.Write(x[0]); //prints "Hello"
}
static void MyMethod(string[] y){
y[0] = "Hello";
}
In this demo code above we start out with an array of size 10 that is referred to by a variable x. In memory it looks like:
x --refers to--> arraydata
When you call MyMethod and pass x in, c# will create another reference y that points to the same data:
x --refers to--> arraydata <--refers to-- y
Now because both references point to the same area of memory anything that you do with y, will also affect what x sees. You put a string (like I did with Hello) in slot 0, both x and y see it. When MyMethod finishes, the reference y is thrown away, but x survives and sees all the changes you made when working with y
The only thing you can't do is point y itself to another different array object somewhere else in memory. That won't change x. You can't do this:
static void MyMethod(string[] y){
y = new string[20];
}
If you do this your useful reference of x and y pointing to the same area of memory:
x ---> array10 <--- y
Will change to:
x ---> array10 y ---> array20
And then the whole array20 and the y reference will be thrown away when MyMethod finishes.
The same rule applies if you call a method that supplies you an array:
static void MyMethod(string[] y){
y = File.ReadAllLines("some path"); //this also points y away to a new array made by ReadAllLines
}
It doesn't matter how or who makes the new array. Just remember that you can fiddle with the contents of an object pointed to by y all you like and the changes will be seen by x, but you can't change out the entire object pointed to by y and hope x will see it
in that case you WOULD have to pass it back when you're done:
static string[] MyMethod(string[] y){
y = new ...
return y;
}
And the main method would have to capture the change:
Main(...){
string[] x = new string[10];
string[] result = MyMethod(x);
}
Now, while I'm giving this mini lesson of "pass by reference" and "pass by value" (which should have been called "pass by original reference" and "pass by copy of reference") it would be useful to note that there is a way to change things so MyMethod can swap y out for a whole new object and x will see the change too.
We don't really use it, ever; there is rarely any need to. Just about the only time it's used is in things like int.Parse. I'm telling you for completeness if education so that if you encounter it you understand it but you should always prefer a "change the contents but not the whole object" or a "if you make a new object pass it back" approach
By marking the y argument with the ref keyword, c# wont make a copy of the reference when calling the method, it will use the original reference and temporarily allow you to call it y:
static void MyMethod(ref string[] y){
y = new array[20];
}
Our diagram:
x ---> array10data
Temporarily becomes:
x a.k.a y ---> array10data
So if you point y to a new array, x experiences the change too, because they're the same reference; y is no longer a different reference to the same data
x a.k.a y ---> array20data
Like I say, don't use it- we always seek to avoid it for various reasons.
Now, I said at the start "you don't need to" - by that, and for the reasons above, I meant you don't need to return anything from this method
Your method receives the array it shall fill (from the file) as a parameter; it doesn't make a new array anywhere so there isn't any need to return the array when done. It will just put any line longer than 4 chars into an array slot. It could then finish without returning anything and the method that called this method will see the changes it made in the array. This is just like my code, where MyMethod changes slot 0 of the array, MyMethod was declared as void so it didn't need to make a return statement , and my Main method god could still see the Hello that I put in the array. In the same vein, your Main method will see all those lines from the file if you make your ReadFileName method (which should perhaps be called FillArray) because it fills the array called name
The most useful thing your method could return is actually an integer saying how many lines were read; the array passed in is of a fixed size. You can't resize it because that entails making a new array which won't work for all those reasons I talked about above. If you were to make a new array and return it there wouldn't be any point in passing an array in.
There are thus several ways we could improve this code but to my mind they come down to two:
don't pass an array in; let this method make a new array and return it. The new array passed back can be exactly sized to fit
keep with the "pass an array in" idea and return an integer of how many lines were actually read instead
For the second idea (which is the simplest to implement) you have to change the return type to int:
static int ReadFileName(string[] name)
And you have to return that variable you use to track which slot to put the next thing in, counter. Counter is always 1 greater than the number of things you've stored so:
return counter - 1;
Your calling method can now look like:
string[] fileData = new string[10000]; //needs to be big enough to hold the whole file!
int numberOfLinesRead = ReadFileName(fileData);
Can you see now why ReadFileName is a bad name for the method? Calling it FillArrayFromFile would be better. This last line of code doesn't read like a book, it doesn't make sense from a natural language perspective. Why would something that looks like it reads a file name (if that even makes sense) take an array and return an int - calling it ReadFileName makes it sound more like it searches an array for a filename and returns the slot number it was found in. Here ends the "name your methods appropriately 101"
So the other idea was to have the Read method make its own array and return it. While we are at it, let's call it ReadFileNamed, and have it take a file path in so it's not hard coded to reading just that one file. And we will have it return an array
static string[] ReadFileNamed(string filepath)
^^^^^^^^ ^^^^^^^^^^^^^^^
the return type the argument passed in
Make it so the first thing it does is declare an array big enough to hold the file (there are still problems with this idea, but this is programming 101; I'll let them go. Can't fix everything using stuff you haven't been taught yet)
Put this somewhere sensible:
string lines = new string[10000];
And change all your occurrences of "name" to be "lines" instead - again we name our variables we'll just like we name our methods sensibly
Change the line that reads the fixed filename to use the variable name we pass in..
using (StreamReader file = new StreamReader(filepath))
At the end of the method, the only thing left to do is size the array accurately before we return it. For a 49 line file, counter will be 50 so let's make an array that is 49 big and then fill it using a loop (I doubt you've been shown Array.Copy)
string[] toReturn = new string[counter-1];
for(int x = 0; x < toReturn.Length; x++)
toReturn[x] = lines[x];
return toReturn;
And now call it like this:
string[] fileLines = ReadFileNamed("student marks.txt");
If you're looking to return name[50] and you know that will be populated, why not go with:
static string readfileName(string[] name)
{
using (StreamReader file = new StreamReader("StudentMarks.txt"))
{
int counter = 0;
string ln;
while ((ln = file.ReadLine()) != null)
{
if (ln.Length > 4)
{
name[counter] = ln;
counter++;
}
}
file.Close();
return name[50];
}
}
You're getting the error because your method signature indicates that you're going to return a string, but you're defining name as a string[] in the argument. If you simply select a single index of your array in the return statement, you'll only return a string.
You have defined your method to return a string, yet the code inside is returning name, which is a string[]. If you want it to return a string[], then change the signature to specify that:
static string[] ReadFileName(string[] name)
However, since your method is only populating the array that was passed in, it's not really necessary to return the array, since the caller already has a reference to the array we're modifying (they passed it to our method in the first place).
There is a potential problem here, though
We're expecting the caller to pass us an array of the appropriate length to hold all the valid lines from the file, yet that number is unknown until we read the file. We could return an array of the size they specified with either empty indexes at the end if it was too big, or incomplete data if it was too small, but instead we should probably just return them a new array, and not require them to pass one to us.
Note that it's easier to use a List<string> instead of a string[], since lists don't require any knowledge of their size at instantiation (they can grow dynamically). Also, we no longer need a counter variable (since we're using the Add method of the list to add new items), and we can remove the file.Close() call since the using block will call that automatically (one of the cool things about them):
static string[] ReadFileName()
{
List<string> validLines = new List<string>();
using (StreamReader file = new StreamReader("StudentMarks.txt"))
{
string ln;
while ((ln = file.ReadLine()) != null)
{
if (ln.Length > 4)
{
validLines.Add(ln);
}
}
}
return validLines.ToArray();
}
And we can simplify the code even more if we use some static methods of the System.IO.File class:
static string[] ReadFileName()
{
return File.ReadLines("StudentMarks.txt").Where(line => line.Length > 4).ToArray();
}
We could also make the method a little more robust by allowing the caller to specify the file name as well as the minimum line length requirement:
static string[] ReadFileName(string fileName, int minLineLength)
{
return File.ReadLines(fileName)
.Where(line => line.Length >= minLineLength).ToArray();
}
Well, you are trying to do several thing in one method:
Read "StudentMarks.txt" file
Put top lines into name existing array (what if you have too few lines in the file?)
return 50th (magic number!) item
If you insist on such implementation:
using System.Linq;
...
static string readfileName(string[] name)
{
var data = File
.ReadLines("StudentMarks.txt")
.Where(line => line.Length > 4)
.Take(name.Length);
int counter = 0;
foreach (item in data)
if (counter < name.Length)
name[counter++] = item;
return name.Length > 50 ? name[50] : "";
}
However, I suggest doing all things separately:
// Reading file lines, materialize them into string[] name
string[] name = File
.ReadLines("StudentMarks.txt")
.Where(line => line.Length > 4)
// .Take(51) // uncomment, if you want at most 51 items
.ToArray();
...
// 50th item of string[] name if any
string item50 = name.Length > 50 ? name[50] : "";
Edit: Splitting single record (name and score) into different collections (name[] and score[]?) often is a bad idea;
the criterium itself (line.Length > 4) is dubious as well (what if we have Lee - 3 letter name - with 187 score?).
Let's implement Finite State Machine with 2 states (when we read name or score) and read (name, score) pairs:
var data = File
.ReadLines("StudentMarks.txt")
.Select(line => line.Trim())
.Where(line => !string.IsNullOrEmpty(line));
List<(string name, int score)> namesAndScores = new List<(string name, int score)>();
string currentName = null;
foreach (string item in data) {
if (null == currentName)
currentName = item;
else {
namesAndScores.Add((currentName, int.Parse(item)));
currentName = null;
}
}
Now it's easy to deal with namesAndScores:
// 25th student and his/her score:
if (namesAndScores.Count > 25)
Console.Write($"{namesAndScores[25].name} achieve {namesAndScores[25].score}");

Difference between Reference Type and Value type in case of string in c#

Ok, So I am a beginner in C# and I learned the concept of Reference Type and Value Type. Also I understood that Value type is on Stack where as Reference types are stored in Heap. And then we need to to manually allocate memory to Reference type and all of that. But I am not able to understand below Behaviour. Please guide me.
Example 1:
var arr1 = new int[3] { 1, 2, 3 }; // memory allocated
var arr2 = arr1; // another variable created but pointing to same memory in Heap
arr2[0] = 2; // value in array 2 changed
System.Console.WriteLine("");
foreach (var item in arr1)
{
System.Console.WriteLine(item);
}
System.Console.WriteLine("------------");
foreach (var item in arr2)
{
System.Console.WriteLine(item);
}
Output as :
arr1: 2,2,3
arr2: 2,2,3
Conclusion: Since both were pointing to same memory location. So, when value in Array 2 is changed the value in Array1 also got affected. So far so good.
Now there is one more reference type which is string.
Consider below Example 2:
var Name = "Mosh";
var FName = Name;
FName = "Hello";
System.Console.WriteLine(Name);
System.Console.WriteLine(FName);
Output as:
Mosh
Hello
Expected Output:
Hello
Hello
Since I changed the value for FName I was expecting the Name value also to be changed as both must be pointing to same memory location. It's one of the simplest question on SO, I am a beginner so bear with me.
In the .Net Framework a String is an immutable reference type.
Since a String does not have a pre-defined memory size (as the value types that can be stored in the Stack), it can grow large (approx. 2 billion Unicode characters), and requires dynamic memory allocation. When a String object is created, the actual value is stored within dynamic memory, the Heap.
Immutable means, it cannot be changed after it has been created. Every change to a string will create a new string. This is why all of the String manipulation methods return a string.
Reference types have some overhead on construction and destruction and
garbage collection, because they are created on the heap. Value types
on the other hand have overhead on method calls (if the data size is
larger than a pointer), because the whole object is copied rather than
just a pointer. Because strings can be much larger than the size of a
pointer, they are designed as reference types. Ref.
In your case: When you assign Name to FName: FName = Name both Strings reference the same Object (as shown in the code sample) that contains the string "Mosh" in the Heap. After FName has ben set to another string value it references a different memory location that stores the new string "Hello". Name keeps pointing to original memory location that stores "Mosh" and remains unchanged.
This behavior of String as value type is less obvious when it is used as a parameter that updates the value in the function. As long as the String parameter is not provided by reference it's original value will not be changed (see sample).
using System;
public class Program
{
public static void Main()
{
var Name = "Mosh";
var FName = Name;
Console.WriteLine(Object.ReferenceEquals(Name,FName));
FName = "Hello";
System.Console.WriteLine(Name);
System.Console.WriteLine(FName);
Console.WriteLine(Object.ReferenceEquals(Name,FName));
TestFunc(Name);
System.Console.WriteLine(Name);
RefFunc(ref FName);
System.Console.WriteLine(FName);
}
public static void TestFunc(string test)
{
test = "after passing";
}
public static void RefFunc(ref string test)
{
test = "after passing";
}
}
Your confusion comes in part because you see both these statements as somewhat equivalent in nature. They are not!
arr2[0] = 2;
FName = "Hello";
In the first case, the array that arr2 is pointing to is modified. But, arr2's reference to that array is unchanged. So both arr1 and arr2 keep pointing to the same array and both see the changes to the array.
In the second case however, it is not the object that FName points to that is being changed. That is to say, we are not modifying the "Mosh" string in any way. Rather, we are changing FName's reference to point a different memory location where the immutable string "Hello" resides in the heap. After this point, FName no longer points to the same memory location as Name. Name keeps pointing to the "Mosh"'s memory location, which remains unchanged.
A string in a reference type yes, but by C#'s design it will more or less behave like other primitives / value types.
To get the effect you're mentioned you would have to specify that a value is a pointer, something similar to: string* PName = Name;
Your question, "Is it safe to say they are pointing at the same location in memory?" I'm not actually sure, though I know that strings do not have a fixed address in memory, so you cannot use "&" or "GetHashCode" like you can to test this on integers or other types.
Edit 1:
Well I'll be a monkey's uncle, you're right. Try this:
Console.WriteLine(Object.ReferenceEquals(Name,FName)

How to get a list of mutable strings?

I have the following piece of code
List<String> l = new List<String>();
String s = "hello";
l.Add(s);
s = "world";
When I set up some breakpoints and go through the program, after executing the last line, the value in the list is still hello instead of world.
Shouldn't it equal world ? Isn't a string an object, and am I not just inserting a pointer into the list? Later on if I change the string to point to a different value ("world"), why is my list still referencing the old value?
How can I get my desired effect ?
Thanks a lot!
Strings are immutable so that won't work. When you attempt to set into it, you actually drop the pointer to the old string and create a new one under the hood.
To get the desired effect, create a class that wraps a string:
public class SortOfMutableString
{
public string Value {get;set;}
public SortOfMutableString(string s)
{
Value = s;
}
public static implicit operator string(SortOfMutableString s)
{
return s.Value;
}
public static implicit operator SortOfMutableString(string s)
{
return new SortOfMutableString(s);
}
}
And use this in your list. Then references will point to the class, but you can contain the string value inside. To make it even better, override implicit casting to and from string so you don't even need to see that you are talking to a SortOfMutableString.
Refer to Jon Skeet's answer for undoubtedly a very accurate explanation about string's in C#, I'm not even going to bother!
Alternative class names:
PseudoMutableString
ICantBelieveItsNotMutable
HappyAndReferenceableString
You're changing the s reference to refer to a different String instance.
Strings are immutable; it is impossible to change the existing instance that you added to the list.
Instead, you can create a mutable StringHolder class with a writable String property.
No, it shouldn't equal world. The value of the variable s is a reference. When you call l.Add(s), that reference is passed by value to the list. So the list now contains a reference to the string "hello".
You now change the value of s to a reference to the string "world". That doesn't change the list at all.
It's important to distinguish between three very different concepts:
A variable (which has a name and a value)
A reference (a value which allows you to navigate to an object, or null)
An object
So in particular, the list doesn't know anything about the variable s - it knows about the value which was passed into Add; that value happened to be the value of s at the time Add was called, that's all.
You may find these articles helpful:
Values and references
Parameter passing in C#
No, there are two different references involved. One called s and one that's at List[0]. When you say l.Add(s) you are setting the list reference to the same address as s, but then when you assign s to "world", then s will point to the new string, leaving List[0] pointing to the old string.
If you really want to do something like what you are asking, you'd need to wrap the string in another object that contains a string, so that s and List[0] both refer to that object, and then that object's reference to a string can change and both will see it.
public class StringWrapper
{
public string TheString { get; set; }
}
Then you can do:
var s = new StringWrapper { TheString = "Hello" };
var l = new List<StringWrapper>();
l.Add(s);
s.TheString = "World";
And now l[0].TheString will be world too. This works because in this case we are not changing the reference in List[0] or s, but they contents of the object referred to by s and List[0].
A variable is an object reference, not an object itself. s = "world" says "make s refer to the string "World") - it does not in any way affect the string "hello" that s was previously referring to. Furthermore, strings in C# are always immutable. You can, however, make the first list element (which currently refers to "hello") refer to a different string: l[0] = "world".
The other two answers here did a great job of saying why what you tried didnt' work, but you were looking for a solution for your desired effect. Wrap a string (property) inside of an object. Then you can change that string and it will be reflected in the collection.

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.

Length of string array via reflection

Is it possible to know the length of a string array - without having an object instance - via reflection?
E.g. in this case: 2.
public string[] Key
{
get { return new string[] { Name, Type }; }
}
EDIT: ok, I will not try to do this, it doesn't make much sense.
Perhaps you mean "without having the exact type of the Array". C# Arrays all derive from Array, so you can cast an Array reference to Array and use the Length property.
If you TRULY wants to reflect the property,
var type = typeof(MyClass);
var prop = type.GetProperty("Key");
var method = prop.GetGetMethod();
var body = method.GetMethodBody();
var ils = body.GetILAsByteArray();
from here you'll have to use one of the various libraries to decode bytes to IL OpCodes (for example https://gist.github.com/104001) . The OpCode you are looking for is newarr. The last push of an int32 before the newarr is the size of the array.
You have two things going on there... telling the length of an array is pretty simple once you have an array; you just call .Length (in the case of a vector).
However, you mention an instance, and you are showing an instance property; which makes me think it is the containing object you lack. In which case... no. You can't make a virtcall on a null instance. And trying to use static-call on an instance member of a class is very evil; IIRC the runtime will kick you for this.
You could, however, make it a static property just by adding the static modifier. Then you just pass in null as the instance to reflection.
I guess you mean you want to know the size of the array the property will return if it were called?
I don't think you can do it sensibly.
If the property had a conditional then it could return different sized arrays, so
you'd have to evaluate the property to know the size. Which could have side effects or be dependent on other values in the object (or statics).
Consider this one:-
static public int n;
public string[] Key
{
get {
if (n > 1)
return new string[] { "Name", "Type" };
else
return new string[] { "Name", "Type", "Cheese" };
}
}
Basically, you'd have to run the code.

Categories