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);
Related
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);
I am trying to make a generic method, that adds an object to the given array.
I tried the below code, but I get this error: "A property, indexer or dynamic member access may not be passed as an out or ref parameter"
public void Main()
{
Foo newObject = new Foo();
AddObjectToArray<Foo>(ref _allMyData.FooArray, newObject);
}
public void AddObjectToArray<T>(ref T[] array, T newObject)
{
var list = array.ToList();
list.Add(newObject);
array = list.ToArray();
}
I could solve it by removing the ref and returning the array like this:
_allMyData.FooArray = AddObjectToArray<Foo>(_allMyData.FooArray, newObject);
But it would be much cleaner if I could only use the ref :-)
Am I missing something obvious?
You can't use a property for the ref parameter. You would need to get the reference out, make the call, and put it back:
Foo[] arr = _allMyData.FooArray;
AddObjectToArray<Foo>(ref arr, newObject);
_allMyData.FooArray = arr;
So, you might want to reconsider using an array in the first place. Adding items to an array is very inefficient anyway, as you have to copy the entire array content each time.
Ive just seen a piece of code that uses a generic list class to instantiate itself in the following manner:
var foo = new List<string>(){"hello", "goodbye"};
The curly braces after the contructor are especially confusing. It reminds me somewhat of
var bar = new string[]{"hi","bye"};
but in the past i've wouldve always used:
var foo = new List<string>(new []{"hello", "goodbye"});
Has anybody got a link explaining the syntax in the first line of code? I wouldnt even know where to begin with googling it.
As others have pointed out, that is a collection initializer. Some other features you might not be aware of that were added to C# 3:
A collection initializer constructor may omit the parentheses if the argument list is empty. So new List<int> { 10, 20, 30 } is fine.
An array initialized with an array initializer may in some cases omit the type. For example, var myInts = new[] { 10, 20, 30}; infers that myInts is int[].
Objects may be initialized using a similar object initializer syntax. var c = new Customer() { Name = "Fred" }; is the same as var temp = new Customer(); temp.Name = "Fred"; var c = temp;
The point of these features is to (1) make more things that used to require statements into things that require only expressions; LINQ likes things to be expressions, and (2) to enable richer type inference, particularly for anonymous types.
Finally: there has been some confusion in some of the answers and comments regarding what is required for a collection initializer. To be used with a collection initializer the type must (1) implement IEnumerable (so that we know it is a collection) and (2) have an Add method (so that we can add stuff to it.)
See
http://blogs.msdn.com/b/madst/archive/2006/10/10/what-is-a-collection_3f00_.aspx
for additional thoughts on the design of the feature.
here you go. The keyword is "Array Initializers".
http://msdn.microsoft.com/en-us/library/aa664573(v=vs.71).aspx
or rather "Collection Initializers"
http://msdn.microsoft.com/en-us/library/bb384062.aspx
This is a collection initializer: http://msdn.microsoft.com/en-us/library/bb384062.aspx
The type so initialized must implement IEnumerable and have an Add method. The items in the curly-brace list are passed to the add method; different items in the list could be passed to different Add methods. If there's an Add overload with more than one argument, you put the multiple arguments in a comma-separated list enclosed in curly braces.
For example:
class MyWeirdCollection : IEnumerable
{
public void Add(int i) { /*...*/ }
public void Add(string s) { /*...*/ }
public void Add(int i, string s) { /*...*/ }
//IEnumerable implementation omitted for brevity
}
This class could be initialized thus:
var weird = new MyWeirdCollection { 1, "Something", {5, "Something else"} };
This compiles to something like this:
var temp = new MyWeirdCollection();
temp.Add(1);
temp.Add("Something");
temp.Add(5, "Something else");
var weird = temp;
In his blog post (link posted by Eric Lippert in the comments), Mads Torgersen expresses this concisely:
The list you provide is not a “list of elements to add”, but a “list of sets of arguments to Add methods”. ...[W]e do separate overload resolution against Add methods for each entry in the list.
In the third line of code you provided you are making a new string array, and then passing that string array to the list. The list will then add each of those items to the list. This involves the extra overhead of allocating the array, populating it, and then discarding it.
There is a mechanism for a class to define how to use Collection Initializers to populate itself. (See the other answers) I have never found the need to utilize this for my own classes, but existing data structures such as List, Dictionary, often define them, and they are useful to use.
This is a collection initializer. You can use it on collections with an Add method.
The pair of parentheses before the curly braces is optional.
This is very convenient, because you can use it on collections other than lists, for example on dictionaries:
var x = new Dictionary<int,string> {{1, "hello"}, {2, "world"}};
This lets you avoid a lengthier initialization sequence:
var x = new Dictionary<int,string>();
x.Add(1, "hello");
x.Add(2, "world");
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
This question already has answers here:
Closed 12 years ago.
Possible Duplicate:
List<int> in c#
I have the following program. I am confused about the output.
The line -
Console.WriteLine(listIsARefType.Count) prints 0 instead of 1. Any idea why ?.
class Program
{
static void Main(string[] args)
{
ListTest d = new ListTest();
d.Test();
}
}
class ListTest
{
public void ModifyIt(List<int> l)
{
l = returnList();
}
public void Test()
{
List<int> listIsARefType = new List<int>();
ModifyIt(listIsARefType);
Console.WriteLine(listIsARefType.Count); // should have been 1 but is 0
Console.ReadKey(true);
}
public List<int> returnList()
{
List<int> t = new List<int>();
t.Add(1);
return t;
}
}
Everything in .Net is passed by value by default, even reference types. The difference with reference types is that it's the reference itself that's passed by value. So when you call the ModifyIt() function, you pass a copy of the reference to the function. That function then changes the copied reference. The original list reference is still intact and the list is unchanged. Your code should go like this:
void ModifyIt(List<int> t)
{
t.Add(1);
}
You'll see that now the list does change. You could also do it like this:
void ModifyIt(ref List<int> t)
{
t = returnList();
}
However, you should favor the former style vs the latter. If you already have something like a returnList() function and you really need a function to add those items to an existing list, do it like this:
void ModifyIt(List<int> t)
{
t.AddRange(returnList());
}
Your ModifyIt method name is misleading: it does not modify the list, it replaces it with a new list.
So while List<int> is a reference type you are passing the reference by value. When you change it to point to a new List inside the method that does not affect the reference in the calling method.
You're creating a new instance of List<int> and assigning it. You cannot do that, because when the function returns, the parameter you passed will still be a reference to the old list.
What you need to do is use the ref parameter like so:
public void ModifyIt(ref List<int> l)
{
l = returnList();
}
And then call it like so:
ModifyIt(ref listIsARefType);
You will find that that will function as expected.
The following line:
List<int> listIsARefType = new List<int>();
creates a new list. The number of items in the list is 0. The next line "modifies" the list (but not really, as that is the problem!)
ModifyIt(listIsARefType);
So while you think you've added something, you actually have not. Let's look at what happens when the ModifyIt( ) gets called:
l = returnList();
simply assigns a List<int> of Count 1 to l. But - here's the important part:
The reference l is only the parameter. You have not changed what listIsARefType references. And so, listIsARefType still contains 0 items. l contains 1 item but it is lost as soon as ModifyIt( ) is complete.
Other people can better explain what's happening here, and it's important to know. Jon Skeet has this excellent article on Parameter passing in C#.