Java and C# pass by value differences - c#

I think the code below will make my question clear. If they both pass by value, why are they different.
C# below:
static void Main(string[] args)
{
var list = new List<int> {1, 2, 3,};
ChangeVars(list);
foreach (var i in list)
{
Console.WriteLine(i);
}
Console.Read();
}
private static void ChangeVars(List<int> list)
{
for(int i = 0; i < list.Count; i++)
{
list[i] = 23;
}
}
Returns 23, 23, 23,
Java below:
public static void main(String[] args)
{
List<Integer> goods = new ArrayList<Integer>();
goods.add(1); goods.add(2); goods.add(3);
ChangeThings(goods);
for(int item: goods)
{
System.out.println(item);
}
}
private static void ChangeThings(List<Integer> goods)
{
for(int item: goods)
{
item = 23;
}
}
Returns 1, 2, 3.
I don't understand the discrepancy.

You're seeing the difference between a foreach loop and a regular for loop.
Your Java code only assigns the local item variable; it doesn't actually modify the list.
If you write goods.set(i, 23), you will modify the list.

The "discrepancy" has nothing to do with passing by value. You are using a different kind of loop in Java, the one where the list is not modified. That's why you see the difference.
If you replace the Java loop with a for (int i = 0 ... ) kind or replace the C# loop with a foreach, there would be no differences between the two programs. In fact, C# program will not compile, because assigning the loop variable is a very common error:
foreach(int item in list)
{
item = 23; // <<=== This will not compile in C#
}

The list is passed by reference in both cases. Just, but
for(int item: goods)
{
item = 23;
}
doesn't extract a reference to the items from the list but copy it to a variable, you would get the same in C# if you wrote x=list[i] followed be x=23.
you must use
for(int i=0;i<goods.size() )
{
goods.set(i, 23);
}

Related

Formatting values from a list

Before getting into C# I used to study Python. I did this small project where the program makes a list and whatever you type it is added to the list. I tried to do the same in C# as a form of practice, but I seem to be doing something, if not everything, for the program won't work. This is my code:
using System;
using System.Collections.Generic:
class idk
{
static void Main(string[] args)
{
List<string> list = new List<string>();
for ( ; true; )
{
Console.Write("Tell me something: ");
var inp = Console.ReadLine();
list.Add(inp);
var i = 1;
foreach (string a in list)
var ni = i + 1;
Console.WriteLine(String.Format("{0}. {1}", ni, a));
}
}
}
So I want my program to take the input, save it in the list, and then print the list but in the form of (e.g.)
Idk
C
A
Like an actual list, where you number each item and all that. So I tried to do the same I did in Python:
List = []
while True:
item = input("Tell me something: ")
List.append(item)
i = 1
for val in List:
print(f"{i}. {val}")
i += 1
Is there something I can edit from my code to make it do the same? Or perhaps I may need to rewrite the whole thing? Thanks for your help.
I suggest loop type changing from foreach to for:
for (int i = 0; i < list.Count; ++i)
Console.WriteLine($"{i + 1}. {list[i]}");
If you insist on foreach:
int index = 0;
foreach (var item in list)
Console.WriteLine($"{++index}. {item}");
You can print the list in one go with a help of Join (to join list's items) and Linq (to query the list):
Console.WriteLine(string.Join(Environment.NewLine, list
.Select((value, index) => $"{index + 1}. {value}")));
I think you should only print the contents of the list at the end when the user has decided they don't want to enter anything anymore:
using System;
using System.Collections.Generic;
class MainClass {
public static void Main (string[] args) {
List<string> strList = new List<string>();
while (true) {
Console.Write("Tell me something: ");
var str = Console.ReadLine();
if (String.IsNullOrWhiteSpace(str)) {
break;
}
strList.Add(str);
}
for (var i = 0; i < strList.Count; i++) {
Console.WriteLine(String.Format("{0}. {1}", i + 1, strList[i]));
}
}
}
Example Usage:
Tell me something: idk
Tell me something: C
Tell me something: A
Tell me something:
1. idk
2. C
3. A
Try it here.

index out of range in For loop despite having enough items in array

I was trying to test this case of resusing the loop variable in foreach in c# as mentioned in following question
Is there a reason for C#'s reuse of the variable in a foreach?
After going through this and this, i tried to reproduce this error with for loop as foreach part has been fixed in c# releases.
But to my surprise, when i tried this code, I got an "Index was outside the bounds of the array." exception from code. Although my array has 4 items and i am trying to access the 3rd index.
public static void Main()
{
var strings = new string[] { "sd2", "dafs3", "dasd5", "fdf6" };
var actions = CreateActions(strings);
actions.ForEach(f => f());
}
private static List<Action> CreateActions(string[] strings)
{
var actions = new List<Action>();
for (int i = 0; i < 4; i++)
{
Console.WriteLine(i);
var fiber = strings[i];
actions.Add(() => Console.WriteLine(strings[i]));
}
return actions;
}
Then I changed my code like this
public static void Main()
{
var strings = new string[] { "sd2", "dafs3", "dasd5", "fdf6" };
var actions = CreateActions(strings);
actions.ForEach(f => f());
}
private static List<Action> CreateActions(string[] strings)
{
var actions = new List<Action>();
for (int i = 0; i < 4; i++)
{
Console.WriteLine(i);
var fiber = strings[i];
actions.Add(() => Console.WriteLine(fiber));
}
return actions;
}
This code is running fine and i got no out of range exception which is strange. Also regarding reusing variable my case was proved.
My first code has this output if run upto index 2 in for loop as for index 3 it throw out of range exception.
0
1
2
fdf6
fdf6
fdf6
My second code piece gave this output and list all 4 items in output
0
1
2
3
sd2
dafs3
dasd5
fdf6
Is there any explanation with c# or any issue with my test code.

Discrepancy in C# LINQ results

When I do this:
currentPage = metadataResponse.ApplicationType.Pages.Find(
page => page.SortOrder == ++currentPage.SortOrder);
The value of currentPage is null.
But the same logic, when I assign the increment value to an integer variable, and then try to get the currentPage
int sortOrder = ++currentPage.SortOrder;
currentPage = metadataResponse.ApplicationType.Pages.Find(
page => page.SortOrder == sortOrder);
currentPage gets populated.
Does anyone have a good answer as to why one works and the other doesn't?
Note: I assume Find method is applied to a collection of values.
In the first code example, you are incrementing currentPage for each element in your collection (this is happening because lambda expressions introduce closures over variables captured from outer scope - see below code block for more info about that). In the second code example, currentPage is incremented only once. Take a look how the following program behaves:
class Program
{
static void Main(string[] args)
{
Func1();
Console.WriteLine("\n");
Func2();
Console.ReadKey();
}
private static void Func1()
{
int i = 0;
var list = new List<int> { 1, 2, 3 };
list.ForEach(x => Console.WriteLine(++i));
}
private static void Func2()
{
int i = 0;
int j = ++i;
var list = new List<int> { 1, 2, 3 };
list.ForEach(x => Console.WriteLine(j));
}
}
Here is some more info about closures in lambda expressions. Have fun! ;-)

Foreach going out of bounds while searching through array c#

The purpose of my program is to take a data txt file and edit it, and/or make additions and subtractions to it.
The text file format is like this:
Name|Address|Phone|# of people coming|isRSVP
The code I have seems to be doing it's job all the way up until I try to click one of the names within a listbox and it needs to search through the multidimensional array to pull information out and place within a set of textboxes where they can be edited. The problem is that the foreach loop I use gives me an out of bounds exception. I tried to do a step into debug to make sure the data is correct in the array and see the process. Everything seems to do correctly but for some reason in the conditional statement person[0]==listbox1.selectedIndex isn't returning true even though both are the same as I seen through the step into process. Any help would be greatly appreciated.
This is my code:
StringBuilder newFile = new StringBuilder();
static string txtList= "guest_list.txt";
static string[] file = File.ReadAllLines(txtList);
static int x = file.Count();
string[,] list = new string[x ,5];
public void loadGuestList()
{
int count2 = 0;
foreach (string line in file)
{
string[] sliced = line.Split('|');
int count = 0;
list[count2, count] = sliced[0];
count++;
list[count2, count] = sliced[1];
count++;
list[count2,count] = sliced[2];
count++;
list[count2,count]= sliced[3];
count++;
list[count2, count] = sliced[4];
count++;
listBox1.Items.Add(list[count2,0]);
count2++;
}
}
private void listBox1_SelectedIndexChanged(object sender, EventArgs e)
{
foreach (string person in list)
{
if ( person[0].ToString()==listBox1.SelectedItem.ToString())
{
addTxt.Text = char.ToString(person[1]);
textPhone.Text = char.ToString(person[2]);
textPeople.Text = char.ToString(person[3]);
if (person[4] == 'n' )
{
}
else
{
chkRSVP.Checked = true;
}
break;
}
}
}
The problem lies in this line:
foreach (string person in list)
The list is defined as being string[,] which when you for each over will do every element, not just the column of data. You really should do something such as:
for(int index = 0; index <= list.GetUpperBound(0); index++)
{
string slice1 = list[index, 0];
string slice2 = list[index, 1];
....
}
or switch to using a Dictionary<string, string[]>().
Try to use a "Person" object and override equals(). Right now you're trying to put your multidimensional array (list[0]) into a string, it'll give you a unwanted result. You should use list[0,0] instead.
In agreement with Adam Gritt, I tested the following code and it seemed to work:
using System;
namespace so_foreach_bounds
{
class MainClass
{
public static void Main (string[] args)
{
//System.Text.StringBuilder newFile = new System.Text.StringBuilder();
string txtList= "guest_list.txt";
string[] file = System.IO.File.ReadAllLines(txtList);
int x = file.Length;
System.Collections.Generic.List<string[]> list = new System.Collections.Generic.List<string[]> ();
foreach (string line in file)
{
string[] sliced = line.Split('|');
list.Add(sliced);
}
foreach (string[] person in list)
{
Console.WriteLine (String.Join(", ", person));
if (person[0] =="Gary")
{
string txtAdd = person[1];
string txtPhone = person[2];
string txtpeople = person[3];
if (person[4] == "n" )
{
}
else
{
bool has_resvped = true;
}
break;
}
}
}
}
}
The issue is how you are iterating over the 2d array. It is usually a better idea to create a "Person" class, than try to manage it via arrays though. Oh yes, and it's usually a good idea to check that a list box item is selected before attempting to use one of its members.

How can I access the next value in a collection inside a foreach loop in C#?

I'm working in C# and with a sorted List<T> of structs. I'm trying to iterate through the List and for each iteration I'd like to access the next member of the list. Is there a way to do this?
Pseudocode example:
foreach (Member member in List)
{
Compare(member, member.next);
}
You can't. Use a for instead
for(int i=0; i<list.Count-1; i++)
Compare(list[i], list[i+1]);
You could just keep the previous value instead:
T prev = default(T);
bool first = true;
foreach(T item in list) {
if(first) {
first = false;
} else {
Compare(prev, item);
}
prev = item;
}
If one were so inclined, you could probably write an Extension method for this as well...
public static void ForEachNext<T>(this IList<T> collection, Action<T, T> func)
{
for (int i = 0; i < collection.Count - 1; i++)
func(collection[i], collection[i + 1]);
}
Usage:
List<int> numList = new List<int> { 1, 3, 5, 7, 9, 11, 13, 15 };
numList.ForEachNext((first, second) =>
{
Console.WriteLine(string.Format("{0}, {1}", first, second));
});
Use a regular for loop with an index, and compare list[i] and list[i+1]. (But make sure to only loop until the second-to-last index.)
Or, if you really want to use a foreach, you can keep a Member reference to the previous member and check the next time around. But I wouldn't recommend it.
LINQ might be your friend here. This approach will work with anything that's IEnumerable<T>, not just IList<T> collections, which is very useful if your collection never ends or is otherwise calculated on-the-fly:
class Program {
static void Main(string[] args) {
var list = new List<Int32> { 1, 2, 3, 4, 5 };
foreach (var comparison in list.Zip(list.Skip(1), Compare)) {
Console.WriteLine(comparison);
}
Console.ReadKey();
}
static Int32 Compare(Int32 first, Int32 second) {
return first - second;
}
}
XmlNode root = xdoc.DocumentElement;
XmlNodeList nodeList = root.SelectNodes("descendant::create-backup-sets/new-file-system-backup-set");
for (int j = 0; j < nodeList.Count; j++ )
{
for (int i = 0; i <= nodeList.Item(j).ChildNodes.Count - 1; i++)
{
if (nodeList.Item(j).ChildNodes[i].Name == "basic-set-info")
{
if (nodeList.Item(j).ChildNodes[i].Attributes["name"].Value != null)
{
// retrieve backup name
_bName = nodeList.Item(j).ChildNodes[i].Attributes["name"].Value.ToString();
}
}
You can do it by IndexOf but FYI IndexOf get the first item equal to the item so if you have a lot of items with the same value it's better to use for loop instead or define the range of search. Official docs are here
foreach (Member member in List)
{
var nextItem = List[List.IndexOf(member)+1];
Compare(member, nextItem);
}

Categories