Lists getting cleared unexpectedly after assignment [duplicate] - c#

This question already has answers here:
How do I clone a generic list in C#?
(29 answers)
C# Value and Reference types
(6 answers)
Closed 4 years ago.
class ListHolder
{
public List<char> List;
public ListHolder(List<char> l)
{
this.List = l;
}
}
class Program
{
static void Main(string[] args)
{
List<char> a = new List<char>();
a.Add('s');
ListHolder c = new ListHolder(a);
a.Clear();
Console.WriteLine(c.List.Count);
}
}
I've put some list into that class, than I cleared the list and wrote the count of the list in the class... I would expect that the output should be "1" (as the list in the class contains the letter 's') but instead it writes "0". How is possible, that a.Clear clears even the list in the class? How can I achieve clearing only the list in the Main and the list in the class letting be?

Since you are passing a reference instead of the list itself, you will get 0 after clearing your list.
What you have to do is passing to the class a new List containing the other list's values:
cl c = new cl(new List<char>(a));
This way, even if you clear the 'main' list, in your class you'll have 1 as items count.
Hope this helps.

List and other classes are reference types. In few words, it means you have an object somewhere in memory and a reference(s) on it.
this.l = l; means you copied the reference to the first list to the class field. So you have one list and two references on it. And when you clear the list via a variable, no matter how you address it after clearing - via a or cl.l. Your single list is cleared already.
If you want to avoid this, you need to create a copy of list in your constructor:
public cl(List<char> l)
{
this.l = new List<char>();
this.l.AddRange(l);
}
}
I recommend you to read more information about reference types. They are used widely and knowledge about them will give you a good base for programming skills.

if (a is System.ValueType)
{
//never
Console.WriteLine("List is value type");
}
if ('s' is System.ValueType)
{
//always
Console.WriteLine("char is value type");
}
I think you know, char is value type, but list is reference type.
Even code like this; it would be same.
List<char> a = new List<char>();
a.Add('s');
List<char> c = a;
a.Clear();
Console.WriteLine(c.Count);

Related

LINQ Select with a method that returns a type - creating a new list [duplicate]

This question already has answers here:
Convert a list to a string in C#
(14 answers)
Closed 9 months ago.
I am a mere beginner and I am trying to learn a bit of LINQ. I have a list of values and I want to receive a different list based on some computation. For example, the below is often quoted in various examples across the Internet:
IEnumerable<int> squares = Enumerable.Range(1, 10).Select(x => x * x);
here the "computation" is done by simply multiplying a member of the original list by itself.
I wanted to actually use a method that returns a string and takes x as an argument.
Here is the code I wrote:
namespace mytests{
class program {
static void Main (string[] args)
{
List<string> nums = new List<string>();
nums.Add("999");
nums.Add("888");
nums.Add("777");
IEnumerable<string> strings = nums.AsEnumerable().Select(num => GetStrings(num));
Console.WriteLine(strings.ToString());
}
private static string GetStrings (string num){
if (num == "999")
return "US";
else if (num == "888")
{
return "GB";
}
else
{
return "PL";
}
}
}
}
It compiles but when debugging, the method GetStrings is never accessed and the strings object does not have any members. I was expecting it to return "US", "GB", "PL".
Any advice on what I could be doing wrong?
Thanks.
IEnumerable<string>.ToString() method does not work as you expected. Result will be
System.Collections.Generic.List`1[System.String]
If you want to see the values which are held in the collection, you should create iteration.
foreach (var i in strings)
Console.WriteLine(i);
This line does two things for you. One of them is writing the values which are held in the collection to console. The other operation is iterating the collection. During iteration, values are needed and linq will execute the necessary operation (in your case GetStrings method).
Currently your code does not use the collection values, so the code does not evaluate the values and does not trigger GetStrings method.

Associate variable to a list [duplicate]

This question already has answers here:
Interesting "params of ref" feature, any workarounds?
(5 answers)
Closed 7 years ago.
I'm not sure if this is easy possible in C#. But I would like to get to know how this could be done easily.
public partial class Form1
{
// I left out the unimportant code for this example
private myControl cLeft,cTop,cBottom,cRight;
private List<myControl>mControls;
public Form1()
{
InitializeComponents();
//this list should contain the fields cLeft,cTop,cBottom,cRight...
mControls=new List<myControl>(){cLeft,cTop,cBottom,cRight};
/* now I want that cLeft and so on get assigned...
of course, this doesn't work because the list refers to the values of
cLeft ... which are null. So I would need to store a reference to those fields to get this work.*/
mControls.ForEach(x=>x=new myControl(this));
}
}
I'm sure it could be done through reflection, but I assume that there should be a way to do this easily in C# or isn't it possible?
It's just a simple loop, there is no need to use LINQ. You just need a for loop.
for (int i = 0 ; i < mControls.Count ; i++) {
mControl[i] = new myControl(this);
}
But, there is no need to write cLeft, cTop etc. You can just refer to them using the indexer: mControls[0], mControl[1] etc.
And remember, the foreach loop or the ForEach extension method doesn't work. This is because you are changing the reference of the variable. That is just another confusing (for beginners) feature of reference types!
Consider this method
public void ChangeReference (string s) {
s = "Hello";
}
And you call this method:
String s = "xxx";
ChangeReference (s);
Will s be "Hello" after the call? No. In the method, you are changing the location of the string in memory, but the argument is still in the same place!

C# modifying a list in a function [duplicate]

This question already has answers here:
Directly modifying List<T> elements
(6 answers)
Closed 8 years ago.
I am writing a function which is passed in a list which is partially filled. I'd like to set some of the fields within the list inside this function. I thought that passing it as a reference would allow me to do this, however, I get the following error:
Error 1 Cannot modify the return value of 'System.Collections.Generic.List.this[int]' because it is not a variable
I am wondering what I might need to do to tell C# that I wish to have the option of modifying the contents of the list.
Here is a summarized version of my code:
public static void Determine_RTMM_Descriptor(ref List<Struct_Descriptor_Type> symbols, string Dwarf_Output_Filename)
{
...
lines = System.IO.File.ReadAllLines(Dwarf_Output_Filename);
//loop on symbol names
for (int idx = 0; idx < symbols.Count; idx++)
{
if(symbols[idx].size == 0)
symbols[idx].size = (int)new System.ComponentModel.Int32Converter().ConvertFromString(split_line[DwarfInterface.SIZE_INDEX]);
...
}
Thanks in advance for any help.
The underlying issue here is that you have a list of value types. When you use the indexer of the list to get an item from the list you are getting a copy of that type. The code symbols[idx] is the value of that item. It is not a variable representing that item, as the error message is telling you.
You're trying to mutate the size of the copy, which will have no effect on the item of the list. This is such a common mistake that the compiler even makes this an error.
If you really are sure that you want to have a mutable value type (hint: you aren't, and you shouldn't have one; you almost certainly just want to have a class here to avoid this problem entirely) then you would need to get the value of the item, mutate it, and then set the item again:
if(symbols[idx].size == 0)
{
var symbol = symbols[idx];
symbol.size = 42;
symbols[idx] = symbol;
}
Your return type on the function is "void" when you should set the return type to the list. That should allow you to change it and return it modified.

C# List Objects

I have a list of Character objects I made and a list of Cells which can contain one character. Is it possible for my Character object to be added to the list and be assigned to a cell and changes I make to it in the party list or the cell to effect the object in both place? I don't really know how the pointers will work out for this. I figure what will happen is the object in the list is a separate object from the one assigned to the cell.
Sorry my code is very large so I don't want to post it all here but the Character I am talking about is a custom class I made. I suppose my question really boles down to two questions. When I put something in a list is that changing where the pointer points or is that a new object all together. Also can I have multiple pointer if I add something to a list then assign that to another instance of the character object will referencing the Character from the Cell Object be the Same as referencing my Character from the other list object.
As long as your Character is a class (i.e. a reference type) then you are essentially storing references to Character objects. Any changes you make to an object through a reference to it will be visible when the object is accessed through any other reference.
Example:
class Character
{
public string Name { get; set; }
}
var c = new Character();
var c2 = c;
var arr1 = new Character[] { c };
var arr2 = new Character[] { c };
arr1[0].Name = "Foo";
Console.WriteLine(arr2[0].Name); // "Foo"
Console.WriteLine(c2.Name); // also "Foo"
So basically you want to treat Characters as a reference type.
Just create your own wrapper object which contains a character inside it.
public MyCharacter
{
public char character{get;set;}
}
Objects are treated by reference, so that should work for you.

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.

Categories