List Collection object as Method Parameter - c#

Can anyone explain how the memory allocation is done while invoking a method having list collection as parameter. Since the code snippet below though apparently seems to result same but it is not resulting same.
So I would like to know the difference in both the method call in terms of memory allocation.
using System;
using System.Collections.Generic;
namespace ListSample
{
class ListSampleClass
{
static void Main(string[] args)
{
List<int> i = new List<int>();
i.Add(10);
i.Add(15);
SampleMethod1(i);
Console.WriteLine("Result of SampleMethod1:"+i[0]);
SampleMethod2(i);
Console.WriteLine("Result of SampleMethod2:" + i[0]);
Console.ReadKey();
}
public static void SampleMethod1(List<int> i)
{
List<int> j = new List<int>();
j.Insert(0,20);
i = j;
}
public static void SampleMethod2(List<int> i)
{
List<int> j = new List<int>();
j = i;
j.Insert(0, 20);
}
}
}

Unless you specify ref or out, parameters are passed by value. For reference types, that means a reference to the object (the List<int> in this case) is passed by value.
"Pass by value" means that the argument (the expression in the calling statement) is evaluated, and then the resulting value is copied into the parameter (the variable listed in the method signature). Any further changes to the parameter, in terms of assigning it a new value, are not seen by the caller. (But keep reading...)
That means that in your first method call:
public static void SampleMethod1(List<int> i)
{
List<int> j = new List<int>();
j.Insert(0,20);
i = j;
}
you're creating a new list, inserting a value into it, and then copying the reference to that new list to i - but that has no effect at all. The parameter is effectively just another local variable - a change to the value of the variable itself doesn't affect the caller.
Now compare that with your second method:
public static void SampleMethod2(List<int> i)
{
List<int> j = new List<int>();
j = i;
j.Insert(0, 20);
}
This creates a new list and then immediately ignores it, instead assigning the reference to the list passed in (as i) to j. It then inserts a value into the list. The net result of this method is that a value is inserted into the list. It's equivalent to:
public static void SampleMethod2(List<int> i)
{
i.Insert(0, 20);
}
Note that this is not changing the value of the parameter. It's making a change to the object that the value of the parameter refers to. This is a crucial difference to understand.
I have an article on parameter passing and another one on reference and value types which may help you understand this more.

Related

Returning an Array Manipulates Original Value in C#

This is my first question on the site and I am sure I'll find my answer here.
For school, I was trying to do some basic C# coding for a challenge that was given to us.
Here is the problem:
Normally when I pass a value through a method I don't run into issues. Like so:
static void Main(string[] args)
{
// Declare Integer
int originalInt = 20;
// Call the Method
int multipliedInt = Multiplication(originalInt);
// Prompt
Console.WriteLine("Original: {0} Modified: {1}", originalInt, multipliedInt);
}
// Method
static public int Multiplication(int original)
{
// Quik Maffs
int modifiedValue = original * 2;
return modifiedValue;
}
The above example works just fine. The original value is 20 and the modified value is 40.
However, this changes when I attempt to do that with an array:
static void Main(string[] args)
{
// Declare Original Array
int[] originalArray = new int[] {1, 4, 6, 8, 12};
// Call Method
int[] multipliedArray = Multiplication(originalArray);
// Prompt
Console.WriteLine("Original: [{0}], Multiplied: [{1}]", String.Join(", ", originalArray), String.Join(", ", multipliedArray));
}
// Method
static public int[] Multiplication(int[] original)
{
// New Int
int[] modified = original;
// Loop
for (int i = 0; i < modified.Length; i++)
{
modified[i] *= 2;
}
return modified;
}
The code above returned the modified value twice. It seems like it modifies the original value as well.
Any idea why this is happening?
int is a value type. When you pass a value type to a method, you pass a copy of the value.
Arrays are reference types. When you pass a reference type to a method, you pass a copy of the reference... but both the copy and original still refer to the same object.
Now it seems you may have understood this much, because of this code:
(This is why I re-opened the question... the stock ref-vs-value answer wasn't gonna cut it here)
int[] modified = original;
However, the other thing that happens with reference types is assignments also only copy the reference. So modified and original in that snippet again refer to the same array object.
To fix this, you need to make an actual deep copy of the array. There are several ways to do this. I would tend to write the method this way:
static public IEnumerable<int> Multiplication(IEnumerable<int> original)
{
return original.Select(i => i * 2);
}
...and append a .ToArray() at the end of the method call if and only if I really need a full array (hint: very often it turns out you don't), like this:
int[] multipliedArray = Multiplication(originalArray).ToArray();
or like this:
var multipliedArray = Multiplication(originalArray);
But I understand there are a number of things here that aren't very familiar to a beginner. You might try something more like this:
static public int[] Multiplication(int[] original)
{
int[] modifed = new int[original.Length];
for (int i = 0; i < original.Length; i++)
{
modified[i] = original[i] * 2;
}
return modified;
}

Wierd way C# passes in lists as arguments

So i ran into this bug where C# behaves like i passed in a list by reference and not by value, like it usually does. Let me show an example:
using System;
using System.Collections.Generic;
namespace testprogram
{
class Program
{
static int x;
static List<Coordinate> coordinates;
static void Main(string[] args)
{
x = 10;
coordinates = new List<Coordinate>();
coordinates.Add(new Coordinate(0, 0));
coordinates.Add(new Coordinate(1, 1));
testfunction(x, coordinates);
Console.WriteLine(x);
foreach (var objekt in coordinates)
{
Console.WriteLine(objekt.xpos);
Console.WriteLine(objekt.ypos);
}
Console.Read();
}
static void testfunction(int test, List<Coordinate> objects)
{
test = 4;
foreach (Coordinate obj in objects)
{
obj.xpos = 4;
obj.ypos = 4;
}
}
}
class Coordinate
{
public int xpos;
public int ypos;
public Coordinate(int new_x, int new_y)
{
xpos = new_x;
ypos = new_y;
}
}
}
This code outputs:
10
4
4
4
4
But why? I expected it to be:
10
0
0
1
1
I tried to make an extra List in the Funktion and assign the value of the parameter to it but even that didnĀ“t work. Is there some workaround for this?
The List<T> argument is a reference type which is passed by value.
If you modify any items in the list within the method, these will remain changed when you leave the method.
However, if you reassign the reference inside the method, these changes will be lost outside the method. If you want to pass a parameter by reference, you use the ref keyword:
static void testfunction(int test, ref List<Coordinate> objects)
{
// This will update objects outside the testfunction method
objects = new List<Coordinate>();
}
You can also use the out keyword, which works similar to the ref keyword. The only difference is that you don't have to initialise values that are passed in as out parameters before you call the method, and they must be initialised before leaving the method.
static void Main(string[] args)
{
// No need to initialise variable passed as "out" parameter
List<Coordinate> objects;
testfunction(test, out objects);
}
static void testfunction(int test, out List<Coordinate> objects)
{
// Removing this line would result in a compilation error
objects = new List<Coordinate>();
}
Your expectations are wrong. The list is passed as a value. It means that variable named objects is a value-copy of original list variable. But the list contains references, I mean the contents of the list are references to Coordinate objects. So if you would try to change the list variable like objects = new List<>(), it wouldn't change your original list. But if you change the objects inside the list, the changes are actually applied to the original list.
objects[0] = new Coordinate(5, 5);
objects[0].xpos = 6;
Both of these examples change the original list.
If you want to have the possibility to safely change anything in the list, you have to make a deep clone of it. You can use Clone() method, but it can be tricky, because you must manually clone each object inside the list. This was already answered here:
How create a new deep copy (clone) of a List<T>?
You passed a reference to the list "by value". So, if you change the reference "object" in "testfunction", then "coordinates" will not change (as a pointer). But changing the elements of "object" will affect the elements of "coordinates".

Passing by value?

I am having an issue with C# function argument passing.
I was wondering how would I make a C# function accept a parameter by value (to make a copy of the original object).
I thought that was the default way C# handled these things, but in the following code:
using System;
using System.Collections.Generic;
using System.Linq;
class MaximumElement
{
static void Main(string[] args)
{
Stack<int> numbers = new Stack<int>();
int n = int.Parse(Console.ReadLine());
for (int i = 0; i < n; i++)
{
string input = Console.ReadLine();
switch (input)
{
case "2": numbers.Pop(); break;
case "3": Console.WriteLine(maxElement(numbers)); break;
default:
string[] argz = input.Split(' ');
numbers.Push(int.Parse(argz[1]));
break;
}
}
}
public static int maxElement(Stack<int> stack)
{
int max = stack.Peek();
for (int i = 0; i < stack.Count; i++)
{
if (max >= stack.Peek())
{
stack.Pop();
}
else if (max < stack.Peek())
{
max = stack.Pop();
}
}
return max;
}
}
My maxElement() function actually changes the original stack I pass to it, and the only way to get around it is to manually make a copy of the stack I pass to the function inside the function.
Thanks for any responses in advance :)
Don't mix passing arguments by value or reference with value types and reference types. Its a common beginner's mistake and you need to have a clear understanding of how both things, allthough related in some way, are completely different features of the language.
I'll probably not use precise terminology because english is not my language but I hope I can get the idea across:
Value type: The variable is the value itself. When you write the following: int i = 1; the variable i holds the value 1.
Reference type: The variable is a reference that points to a place in memory where the object is. That means that, when you say string s = "Hello"; s does not contain "Hello", it contains the memory address where "Hello" is stored.
So what happens when you pass an argument by value (default in C#). We have two possibilities:
Argument is a value type: You get a copy of the variable, that means
that if you pass along i = 1 you recieve a copy which also
contains 1, but both are alltogether different objects.
This is obvious when dealing with mutable value types, for example System.Drawing.Point:
Point point = new Point(0, 0);
Frob(point);
var b = point.X == 1 && point.Y == 1; //False, point does not change.
void Frob(Point p) { p.Offset(1, 1); } // p is a copy of point and therefore contains a copy of the value stored in point, not the value itself.
Argument is a reference type: You get a copy of the variable, that means you get a copy of the reference to the memory address, but the object the copy is pointing at is the same. This is the scenario you are in.
Foo foo = new Foo();
foo.Blah = 1;
Frob(foo);
var b = foo.Blah == 2; //True, foo.Blah has been modified.
void Frob(Foo f) { foo.Blah = 2; } //both foo and f point to the same object.
Notice that in both cases what you can't do is modify what the reference is pointing to. This won't work:
string s = "hello";
foo(s);
var b = s == "bye"; //false, s still points to the original string
void Foo(string str)
{
str = "bye";
}
Now, what happens if we pass by reference? Well, the main difference is that you are passing the variable itself, not a copy. That means that in case of a value type you are passing the original value and in case of a reference type, the original address, not a copy. This allows the following:
//Value type
Point point = new Point(0, 0);
Frob(ref point);
var b = point.X == 1 && point.Y == 1; //True, point and p are the same variable.
void Frob(ref Point p) { p.Offset(1, 1); }
and
//Value or reference type
string s = "hello";
foo(ref s);
var b = s == "bye"; //true
void Foo(ref string str)
{
str = "bye";
}
Hope this clarifies the difference.
It is a little complex. From MSDN (https://msdn.microsoft.com/en-us/library/s6938f28.aspx):
A variable of a reference type does not contain its data directly; it contains a reference to its data. When you pass a reference-type parameter by value, it is possible to change the data pointed to by the reference, such as the value of a class member. However, you cannot change the value of the reference itself; that is, you cannot use the same reference to allocate memory for a new class and have it persist outside the block. To do that, pass the parameter using the ref or out keyword. For simplicity, the following examples use ref.
Here is the code example they provide:
static void Change(int[] pArray)
{
pArray[0] = 888; // This change affects the original element.
pArray = new int[5] {-3, -1, -2, -3, -4}; // This change is local.
Now, if you use the ref keyword on your parameter
static void Change(ref int[] pArray)
{
pArray[0] = 888; // This change affects the original element.
pArray = new int[5] {-3, -1, -2, -3, -4}; // This change also affects the original
So, with those things in mind, you could...
public static int maxElement(Stack<int> stack)
{
stack = new Stack<int>(stack); // Now changes will be local
int max = stack.Peek();
for (int i = 0; i < stack.Count; i++)
{
if (max >= stack.Peek())
{
stack.Pop();
}
else if (max < stack.Peek())
{
max = stack.Pop();
}
}
return max;
}
You would need to make a copy of the Stack, which if a shallow copy works you can use Clone() method.

How does the keyword `ref` affect memory management and garbage collection?

I am new to C# and I have been messing around with 'ref', 'out' and pointers, and I have a general question about how 'ref' works, especially when using objects and not primitive types. Say this is my method:
public void foo(ref Point p) {
p.set(1,1); // the x/y values are updated without constructing a new Point
}
and a similar method:
public void bar(Point p) {
p.set(1,1); // the x/y values are updated without constructing a new Point
}
EDIT: Point is a class in both cases
Both work, but is one more cost effective than the other? I know in C++ if you pass in a pointer you are only giving the memory address; from my understanding of C#, you cannot pass in an Object* into a method because of the automatic garbage collection. Does 'ref' pin an object to a location? Also, if you pass in an object to a method, like 'bar' above, is it passing a copy of the object or is it passing a pointer/reference?
Clarification: In my book I have, it does say if you want a method to update a primitive, such as int, you need to use ref (out if it is not initialized) or a *. I was asking if the same holds true for objects, and if passing an object as a parameter rather than a ref to an object costs more.
If your type is a struct, ref is roughly equivalent to a pointer to that struct. No new instances are created here. The difference (from passing it without ref) is that you can now mutate the original struct instance contained in that variable.
If your type is a class, ref simply adds one more level of indirection. No new instances are created here either. The difference (from passing it without ref) is that you can now entirely replace (not just mutate) the original class instance referenced by that variable with something else.
Since no new instances are created in either case, the garbage collector probably won't care about this in any important way.
In fact, class is a reference type, it mean that a variable of a reference type hold a reference to it's data instead of holding is data directly like value type.
When you pass a variable of a reference type as method parameter, it pass the reference to that data, not the data itself. So if update some properties of your object, the update is reflected in the original variable, except if you reassign the parameter.
Example from MSDN :
class PassingRefByVal
{
static void Change(int[] pArray)
{
pArray[0] = 888; // This change affects the original element.
pArray = new int[5] {-3, -1, -2, -3, -4}; // This change is local.
System.Console.WriteLine("Inside the method, the first element is: {0}", pArray[0]);
}
static void Main()
{
int[] arr = {1, 4, 5};
System.Console.WriteLine("Inside Main, before calling the method, the first element is: {0}", arr [0]);
Change(arr);
System.Console.WriteLine("Inside Main, after calling the method, the first element is: {0}", arr [0]);
}
}
/* Output:
Inside Main, before calling the method, the first element is: 1
Inside the method, the first element is: -3
Inside Main, after calling the method, the first element is: 888
*/
Passing a variable of a reference type with the ref keyword will reflect any change to the original variable, even if you reassin the parameter.
Example from MSDN :
class PassingRefByRef
{
static void Change(ref int[] pArray)
{
// Both of the following changes will affect the original variables:
pArray[0] = 888;
pArray = new int[5] {-3, -1, -2, -3, -4};
System.Console.WriteLine("Inside the method, the first element is: {0}", pArray[0]);
}
static void Main()
{
int[] arr = {1, 4, 5};
System.Console.WriteLine("Inside Main, before calling the method, the first element is: {0}", arr[0]);
Change(ref arr);
System.Console.WriteLine("Inside Main, after calling the method, the first element is: {0}", arr[0]);
}
}
/* Output:
Inside Main, before calling the method, the first element is: 1
Inside the method, the first element is: -3
Inside Main, after calling the method, the first element is: -3
*/
MSDN documentation.
Quick distinction between a class vs. a struct:
A class is a reference type. When an object of the class is created,
the variable to which the object is assigned holds only a reference to
that memory. When the object reference is assigned to a new variable,
the new variable refers to the original object. Changes made through
one variable are reflected in the other variable because they both
refer to the same data.
A struct is a value type. When a struct is
created, the variable to which the struct is assigned holds the
struct's actual data. When the struct is assigned to a new variable,
it is copied. The new variable and the original variable therefore
contain two separate copies of the same data. Changes made to one copy
do not affect the other copy.
https://msdn.microsoft.com/en-us/library/ms173109.aspx
Your example is tricky because in c# Point is an immutable struct, not an object.
Hopefully this example will help show what happens with structs and objects with and without ref.
public static void StructTest()
{
var fooStruct = new MyStruct();
var barStruct = new MyStruct();
Console.WriteLine(fooStruct.Value); // prints 0
Console.WriteLine(barStruct.Value); // prints 0
fooStruct(ref fooStruct);
barStruct(barStruct);
// Struct value only changes when passed by reference.
Console.WriteLine(fooStruct.Value); // prints 1
Console.WriteLine(barStruct.Value); // prints 0
}
public void fooStruct(ref MyStruct m)
{
m.Value++;
}
public void barStruct(MyStruct m)
{
m.Value++;
}
public static void ObjectTest()
{
var fooObject = new MyObject();
var barObject = new MyObject();
Console.WriteLine(fooObject.Value); // prints 0
Console.WriteLine(barObject.Value); // prints 0
fooObject(ref fooObject);
barObject(barObject);
// Objects are automatically passed by reference. No difference.
Console.WriteLine(fooObject.Value); // prints 1
Console.WriteLine(barObject.Value); // prints 1
fooSetObjectToNull(ref fooObject);
barSetObjectToNull(barObject);
// Reference is actually a pointer to the variable that holds a reference to the object.
Console.WriteLine(fooObject == null); // prints true
Console.WriteLine(barObject == null); // prints false
}
public void fooObject(ref MyObject m)
{
m.Value++;
}
public void barObject(ref MyObject m)
{
m.Value++;
}
public void fooSetObjectToNull(ref MyObject m)
{
m = null;
}
public void barSetObjectToNull(MyObject m)
{
m = null;
}

Array through parameter by reference affecting another array

CardDetails is a Structure.
public static void ParceIntricaciesJabber(ref CardDetails[] WhichArray)
{
WhichArray[0].ID = 50;
WhichArray[0].Type = "None";
}
In calling:
ParceIntricaciesJabber(ref OpponentCards);
After I call the function though, another Array called PlayerCards is affected in the exact same way as OpponentCards - despite being declared as two different arrays. They have the same number of elements and the same data Type, and that's it.
This probably should be obvious but i'm not seeing it. The code works in VB.NET. What am I doing wrong?
EDIT: Initialization Code:
public static class Module1{
public static CardDetails[] PlayerCards = new CardDetails[100];
public static CardDetails[] OpponentCards = new CardDetails[100];
}
And also when navigating to the Form
for (int n = 1; n <= 100; n++)
{
Module1.PlayerCards[n] = new CardDetails();
Module1.OpponentCards[n] = new CardDetails();
}
My guess is that you are sharing the reference to the arrays. Even though it is structs inside the array, the array itself is a reference type. You will need to post your array instantiation code to verify one way or the other though

Categories