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);
This question already has answers here:
How to modify a foreach iteration variable from within foreach loop?
(4 answers)
Is there any way to do this, assign a value within a List<T>.ForEach() statement?
(4 answers)
Closed 7 years ago.
It seems weird to me. Thought if someone can clear my doubt please.
var list = new List<SomeClass>();
Now if we use ForEach extension method of collection
list.ForEach(c=>c.SomeProperty = SomeValue);
So now we can get the updated value. So it seems to be reference type. And Yes ForEach takes an Action so its reference type. But if try to re-initilize the object inside the ForEach I do not see expected behavior.
list.ForEach(c=>{
if(SomeCondition) // lets consider its always true now.
{
c = new SomeClass();
}
});
So after this execution I was expecting fresh list. And should not contain the reference of old object. But if I print the list. I can see the old values.
So why ForEach didnt re-initialize the objects in list? While we saw ForEach was reference type on value type
Regardless of whether the type of c is a reference type or a value type, when setting a variable c = something, you are overwriting whatever the variable referred to. So the original object that c referred to is no longer referenced. This does not change the list from updating its reference either though, that’s simply not how it works.
If you want to replace some elements in your list by something else, you will need to use Select:
list.Select(c =>
{
if (SomeCondition)
return new SomeClass();
// default case, return the same element
return c;
}.ToList();
Note that this creates a new list, so the old list still contains the same elements.
If you wanted to actually replace elements in the original list, you will have to overwrite the elements in the list by accessing its index. You can do this with a normal for loop:
for (int i = 0; i < list.Count; i++)
{
if (SomeCondition)
{
list[i] = new SomeClass();
}
}
This will actually mutate the existing list and replace some elements inside of it.
This question already has answers here:
How to empty a list in C#?
(7 answers)
Closed 8 years ago.
This has probably been asked somewhere, but I was unable to find it.
I have a List of strings like this :
public static List<string> AnimalsL = new List<string>();
public static void ListAnimals()
{
AnimalsL.Add("Animal one");
AnimalsL.Add("Animal two");
}
public static void ReturnAnimals()
{
string[] Animals = AnimalsL.ToArray();
if(AnimalsL != null)
{
for(int Index = 0; Index < Animals.Length; Index++)
{
System.Windows.Forms.MessageBox.Show(Animals[Index].ToString());
}
}
}
[b] Above is working code [/b]
So, as the title says, my real question is : How to completely empty a List of strings ?
For instance, I could want to store the animal names IF the animals type is snake, or dog, and I wouldn't want them crossing into the same category, or if I want to ensure the list is empty before adding to it, or clear it once I've used my methods on the animal.
I was thinking of declaring Animals[] = string.Empty, but this wouldn't actually clear the list ..
There are two ways to do this.
1.
AnimalsL.Clear(); // clears all the items in the list
2.
AnimalsL = new List<string>(); // assigns a new instance
Actually, creating a new instance is faster than calling the Clear method, though if you want to keep the reference to that object, then calling Clear is better.
Here is a benchmark for Clear() vs new List Link
Use List<T>.Clear() method to remove all entries from a list instance.
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
passing in object by ref
With the code below, the output would be:
Without:
With:1
Code:
static void Main(string[] args)
{
var listWithoutRef = new List<int>();
WithoutRef(listWithoutRef);
Console.WriteLine("Without:" + string.Join(" ", listWithoutRef));
var listWithRef = new List<int>();
WithRef(ref listWithRef);
Console.WriteLine("With:" + string.Join(" ", listWithRef));
}
static void WithoutRef(List<int> inList)
{
inList = new List<int>(new int[] { 1 });
}
static void WithRef(ref List<int> inList)
{
inList = new List<int>(new int[] { 1 });
}
By just looking at this, I would have said that a List is on the Heap, and so is passed by ref anyway, so they should be the same? Am I misunderstanding the ref keyword? Or am I missing something else?
Am I misunderstanding the ref keyword? Or am I missing something else?
Yes. You're not passing the list itself to the method, but rather passing the reference to the list by reference. This lets you change the reference (the List<int> that listWithRef actual refers to) within the method, and have it reflect.
Without using the ref keyword, your method can't change the reference to the list - the actual list storage mechanism is unchanged in either case.
Note that this isn't required if you just want to use the list. You can call List<int>.Add within either method, for example, and the list will get new items added to it. Ref is only required with reference types to change the reference itself.
Yes, all of the List objects are stored on the heap either way. However, without the ref keyword, you can't reassign the inList parameter and have it affect the caller's scope. When you create a new List object, it goes on the heap as a new object, but the original reference in the caller's scope is not affected unless you use the ref keyword.
In WithoutRef, if you call methods on the existing List without redefining it, you will see that it is modified:
inList.Clear();
inList.Add(1);
If I did not get this terribly wrong, this behaviour is strange for me. Rather than explaining, I'll post a sample code below and please tell me why does I get output x and not y.
private void button1_Click(object sender, EventArgs e)
{
List<int> l = new List<int>() { 1, 2, 3 };
Fuss(l);
MessageBox.Show(l.Count.ToString()); // output is 5
}
private void Fuss(List<int> l)
{
l.Add(4);
l.Add(5);
}
Output should, I assume would be 3. But I get the output as 5. I understand the output can be 5 if I do this:
private void button1_Click(object sender, EventArgs e)
{
List<int> l = new List<int>() { 1, 2, 3 };
Fuss(ref l);
MessageBox.Show(l.Count.ToString()); // output is 5
}
private void Fuss(ref List<int> l)
{
l.Add(4);
l.Add(5);
}
It does not act like its passed by ref.
void ChangeMe(List<int> list) {
list = new List<int>();
list.Add(10);
}
void ChangeMeReally(ref List<int> list) {
list = new List<int>();
list.Add(10);
}
Try it. Do you notice the difference?
You can only change the contents of list (or any reference type) if you pass it without a ref (because as others have said, you are passing a reference to the object on the heap and thus change the same "memory").
However you cannot change "list", "list" is a variable that points to an object of type List. You can only change "list" if you pass it by reference (to make it point somewhere else). You get a copy of the reference, which if changed, can only be observed inside your method.
Parameters are passed by value in C# unless they are marked with the ref or out modifiers. For reference types, this means that the reference is passed by value. Therefore, in Fuss, l is referring to the same instance of List<int> as its caller. Therefore, any modifications to this instance of List<int> will be seen by the caller.
Now, if you mark the parameter l with ref or out, then the parameter is passed by reference. What this means is that in Fuss, l is an alias for storage location used as a parameter to invoke the method. To be clear:
public void Fuss(ref List<int> l)
called by
List<int> list = new List<int> { 1, 2, 3 };
Fuss(list);
Now, in Fuss, l is an alias for list. In particular, if you assign a new instance of List<int> to l, the caller will see that new instance assigned to the variable list as well. In particular, if you say
public void Fuss(ref List<int> l) {
l = new List<int> { 1 };
}
then the caller will now see a list with one element. But if you say
public void Fuss(List<int> l) {
l = new List<int> { 1 };
}
and call by
List<int> list = new List<int> { 1, 2, 3 };
Fuss(list);
then the caller will still see list as having three elements.
Clear?
ByRef and ByVal only apply to value types, not to reference types, which are always passed as though they were "byref".
If you need to modify a list discreetly, use the ".ToList()" function, and you'll get a clone of your list.
Keep in mind that if your list contains reference types, your "new" list contains pointers to the same objects that your original list did.
Lists are already reference types, so when you pass them to a method, you are passing a reference. Any Add calls will affect the list in the caller.
Passing a List<T> by ref behaves essentially like passing a double-pointer to that list. Here's an illustration:
using System;
using System.Collections.Generic;
public class Test
{
public static void Main()
{
List<int> l = new List<int>() { 1, 2, 3 };
Fuss(l);
Console.WriteLine(l.Count); // Count will now be 5.
FussNonRef(l);
Console.WriteLine(l.Count); // Count will still be 5 because we
// overwrote the copy of our reference
// in FussNonRef.
FussRef(ref l);
Console.WriteLine(l.Count); // Count will be 2 because we changed
// our reference in FussRef.
}
private static void Fuss(List<int> l)
{
l.Add(4);
l.Add(5);
}
private static void FussNonRef(List<int> l)
{
l = new List<int>();
l.Add(6);
l.Add(7);
}
private static void FussRef(ref List<int> l)
{
l = new List<int>();
l.Add(8);
l.Add(9);
}
}
A variable, parameter, or field of type "List", or any other reference type, doesn't actually hold a list (or object of any other class). Instead, it will hold something like "Object ID #29115" (not such an actual string, of course, but a combination of bits which means essentially that). Elsewhere, the system will have an indexed collection of objects called the heap; if some variable of type List holds "Object ID #29115", then object #29115 in the heap will be an instance of List or some type derived therefrom.
If MyFoo is a variable of type List, a statement like 'MyFoo.Add("George")' won't actually change MyFoo; instead, it means "Examine the object ID stored in MyFoo, and invoke the "Add" method of the object stored therein. If MyFoo held "Object ID #19533" before the statement executed, it will continue to do so afterward, but Object ID #19533 will have had its Add method invoked (probably altering that object). Conversely, a statement like "MyFoo = MyBar" will make MyFoo hold the same object-id as MyBar, but won't actually do anything to the objects in question. If MyBar held "Object ID #59212" before the statement, then after the statement, MyFoo will also hold "ObjectId #59212". Nothing will have happened to object ID #19533, nor object ID#59212.
The difference between ref and non-ref for reference types like List is not whether you pass a reference (that happens always), but whether that reference can be changed. Try the following
private void Fuss(ref List<int> l)
{
l = new List<int> { 4, 5 };
}
and you'll see the count is 2, because the function not only manipulated the original list but the reference itself.
Only primitive types like int, double etc. are passed by value.
Complex types (like list) are passed via reference (which is a passing pointer by value, to be exact).
lets explain it Easy :)
"Fuss" in your first chunk of code knows that he should accept a refrence from a list type
"Fuss" in your second chunk of code knows that he should accept nothing! but just a refrence to "sth else" which accidently this "sth else" is the same thing which was in your first chunk of code 🤣♥
if it seems confusing i think you should have a deep understanding of what a reference is