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.
Related
I want to set a value for array item variable:
string string1;
string string2;
string[] myArray = { string1, string2 };
public Test(){
for(int i=0; i < myArray.Length; i++){
myArray[i] = "hello!";
}
}
If I use myArray[i] = "Hello" method, then it sets to myArray value not string1 and string2 value.
An array in C# contains primitive values (integers, doubles, booleans, …) or references (references to class or struct instances). When you update one item of the array, you are only changing the "value" in the array. You are not updating thte object being pointed to. In other words: you are only swapping the reference to point to a different object, the object is never changed.
To achieve this, you need to insert another level of inderection (think pointer to pointer in good ol' C).
One option to achieve this would be to introduce a wrapper type to hold your value and allow to update it:
class Reference<T> {
T Value { get; set; }
public Reference(T value) {
this.Value = value;
}
}
Reference<string> string1;
Reference<string> string2;
Reference<string>[] myArray = { string1, string2 };
public Test() {
for(int i = 0; i < myArray.Length; i++) {
myArray[i].Value = "hello!";
}
}
you need to initialize the empty string for "string1" & "string2". then it will take the value in it.
string string1="";
string string2="";
string[] myArray = { string1, string2 };
for (int i = 0; i < myArray.Length; i++)
{
myArray[i] = "hello!";
}
You cannot change variables string1 and string2 by manipulating the array.
I hope the following snippet makes it clearer what happens. The important part is that when the array is created it contains references to the objects that the variables point to, not to the variables themselves. a[i] = changes what the array element points to, not what variable it was created from.
// Int is a value type
int i1 = 1;
int[] ai = { i1 }; // the array contains the actual number '1',
// each element of the array is the size of int
ai[0] = 7; // This swaps number 1 to 7 *inside* the array
// C is a class which is a reference type
C c = new C { Property = 1 };
C[] a2 = { c }; // the array contains the reference to 'c'
// Now both c and a2[0] point to the same object
// Importantly, c doesn't control the array
// and the array doesn't control variable c
// They just point to the same object
a2[0].Property = 2; // This changes Property of the object a2 points,
// which is the same object c points to
// Please note that c.Property = 3; would not change the array,
// it would change the object that a[0] also points to.
// However, we can also change what object a[0] points too;
a2[0] = new C { Property = 4 }; // This makes a[0] point to a new object
// In C# string is a reference type
string string1 = "Hello1";
string[] myArray = { string1 };
myArray[0] = "hello!"; // This makes myArray[0] point to another object and that object is a new string "hello!"
I am not exactly sure what you're asking but I think your confusion is about reference and value types.
If you want to write to string1 and string2 using myArray you have to know a thing or two about value and reference types in C#.
For your current usage you can consider string a value type. With this I mean: that if you assign a string to another string (string stringX = a_previous_declared_String) it creates a copy. Changing the copy will not change the original. Storing your string in MyArray also creates a copy.
(Please note that in fact string is a reference type which behaves like a value type when doing assignment (stringX = stringY) operations. If you want to know more, you can e.g. start here. For the purpose of explaining the general difference between value types and reference types, using your example, I simplify an call string a value type here).
If you want to change the original, you have to store a reference to your string in myArray. For this, in C#, you can create a reference type variable which holds your value type. This can be a class which holds your string as a field. Then, you store a reference to your class in MyArray. If you then change the string via MyArray, your original string1 and string2 wil also get modified.
I created the simplest version of this to help you understand. See #knittl his answer for a more general implementation that would work for other value types (like int and float) as well.
My example (which I tested and runs as C# console application):
using System;
namespace TestProgram
{
public class RefTypeString
{
public string MyString;
public RefTypeString(string myString)
{
MyString = myString;
}
}
class Program
{
static void Main(string[] args)
{
RefTypeString string1 = new RefTypeString(null);
RefTypeString string2 = new RefTypeString("initial value");
RefTypeString[] myArray = { string1, string2 };
for (int i = 0; i < myArray.Length; i++)
{
if (i == 0)
{
myArray[i].MyString = "hello!"; //set value for string1 and string2 not an array
}
}
Console.WriteLine("MyArray[0] / string1");
Console.WriteLine(myArray[0].MyString);
Console.WriteLine("MyArray[1] / string1");
Console.WriteLine(myArray[1].MyString);
Console.WriteLine("string1");
Console.WriteLine(string1.MyString);
Console.WriteLine("string2");
Console.WriteLine(string2.MyString);
}
}
}
Console output:
MyArray[0] / string1
hello!
MyArray[1] / string1
initial value
string1
hello!
string2
initial value
I'm making a C# script in Unity. My intention is to create a class Scenario, create classes representing different scenarios, which would then be stored in an array scenarioListAll.
A (simplified) version of the code is as follows:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class OverallManager2 : MonoBehaviour
{
public static object[] scenarioListAll = new object[40];
public class Scenario
{
public string scenarioDesc;
public bool surprise; // The 'surprise' bool I want to reference is defined here
public string surpriseType;
public int[] leftOption;
public int[] rightOption;
public int scenarioNumber;
public Scenario(string st, bool sp, int[] l, int[] r, int n)
{
scenarioDesc = st;
surprise = sp;
leftOption = l;
rightOption = r;
scenarioNumber = n;
}
// I haven't used this, but I'm not sure if this matters so I'm including this too
public Scenario(string st, bool sp, string spt, int[] l, int[] r, int n)
{
scenarioDesc = st;
surprise = sp;
surpriseType = spt;
leftOption = l;
rightOption = r;
scenarioNumber = n;
}
}
public static int[] getArray(int a, int b, int c, int d, int e, int f)
{
int[] arr = new int[6] {a, b, c, d, e, f};
return arr;
}
// Storing scenarios, am looking for the bool (2nd position)
public Scenario s1 = new Scenario("Test1", false, getArray(1, 1, 1, 1, 1, 1), getArray(1, 1, 1, 1, 1, 1), 1);
public Scenario s2 = new Scenario("Test2", true, getArray(1, 1, 1, 1, 1, 1), getArray(1, 1, 1, 1, 1, 1), 2);
public Scenario s3 = new Scenario("Test3", false, getArray(1, 1, 1, 1, 1, 1), getArray(1, 1, 1, 1, 1, 1), 3);
void Awake()
{
// Store scenarios in object array
scenarioListAll[0] = s1;
scenarioListAll[1] = s2;
scenarioListAll[2] = s3;
for(int i = 0; i < 40; i++)
{
object temp = scenarioListAll[i]; // Trying to extract the object stored in the array in a temp object
bool surpriseCheck = temp.surprise; // I am having problems with this line
if(surpriseCheck == true)
{
// Do something
}
}
}
// Ignoring start and update since they're irrelevant in this context
}
What I would like to do is to check whether the surprise element within a newly defined scenario (e.g. s1) is true. To do that, I was planning to extract the scenario stored in the array scenarioListAll, and then extract the surprise component from there. However, I'm couldn't figure out how to do this (e.g. in the code shown above, it returns Compiler Error CS1061).
I don't think I was able to find any documentation on this either, but I might not have understood something. I'm learning on my own so please bear with my poor knowledge/presentation.
Thank you for your time. Your help is much appreciated.
You are having a compilation issue due to the fact that the c# compiler doesn't know that temp is a Scenario since you declared it as "object". If you want to loop through the scenarios and check to see if they are a surprise you can use something like this:
foreach(Scenario temp in scenarioListAll)
{
bool surpriseCheck = temp.surprise;
if(surpriseCheck == true)
{
// Do something
}
}
Another way of accomplishing the same task with more control over the iteration would be:
for(int i = 0; i < scenarioListAll.Length; i++)
{
Scenario temp = scenarioListAll[i];
bool surpriseCheck = temp.surprise;
if(surpriseCheck == true)
{
// Do something
}
}
The benefit of the first version is that you don't have to worry about overrunning the bounds of the array. As Mike added below you could also use var to have the compiler fill in the type for you.
It's sometimes easiest to allow the compiler to determine the type of a variable for us.
In your case, you've specified that the variable temp will be of type object. Now, that's fine, but while a Scenario derives from object, and object is the lowest level class in the .Net environment, and is not a Scenario.
The var keyword doesn't mean that the declared type is of a "variable" type, instead it's telling the compiler just to "fill in" the correct type, based on the action you're taking. So, to put this in to action in your case, you could do this instead:
for( var i = 0; i < 40; i++ ) // notice the use of var here as well
{
var scenario = scenarioListAll[i]; // renamed temp to scenario
// var surpriseCheck = scenario .surprise; // var used but line not required
if( scenario.surprise )
{
// Do something
}
}
I went overboard there to demonstrate that the compiler is quite happy with the var keyword just about wherever you'd specify a data type as a type for a variable. Obviously not when you're trying to cast types, and there ARE sometimes where you'd want to specify the exact type you're trying to instantiate.
In your case, your next issue will be that you've defined the array as having 40 object elements, but you've then only instantiated 3 Scenario elements (which is valid, but probably not quite what you want overall). So your code, as it stands, is going to NullReference error out. You'll be able to avoid that, with a small modification, so your amended code could look like this, to include some type checks:
for( var i = 0; i < scenarioListAll.Length; i++ )
{
// First, check to see if the object in the array cell is a Scenario.
// If the item in the cell is a Scenario, check to see if surprise is set.
if ( scenarioListAll[i] is Scenario scenario && scenario.surprise )
{
// Do something
}
}
for more information on the is keyword, check the Microsoft Docs here.
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;
}
i have the following array :
int[] myArray = {21,21,364,658,87};
and a reference to the second element like so:
int rr = myArray[1];
i want something like :
rr = 500
Console.writeLine(myArray[1]);// ---> should print 500 !
i hope you guys got my idea , i can do this easily in python like the example above.
so
how to do this in C#
my solution would probably be create property with arr[1] as its backing property
something like:
public int rr
{
set{ arr[1] = value;}
get{ return arr[1];}
}
and than rr=500; will be the same as arr[1]=500;
You could use something like this:
public static class ArrayExtensions
{
public static Action<int> CreateSetter(this int[] array, int index)
{
return (value) => array[index] = value;
}
}
[TestFixture]
public class ArrayTest
{
[Test]
public void Test()
{
int[] myArray = {21,21,364,658,87};
Action<int> rr = myArray.CreateSetter(1);
rr(500);
Assert.AreEqual(500, myArray[1]);
}
}
When you do this:
int[] myArray = {21,21,364,658,87};
int rr = myArray[1];
rr = 500;
You will only overwrite the value in rr, there is no way for you to get the actual memory address of an arrays inner elements, and thereby updating it.
My answer must therefore be:
myArray[1] = 500;
I'm trying to understand what you're trying to do, if you want to encapsulate your change in a function you could pass the reference on this way, but it's all about what you want to do with it:
public void Proc()
{
var ints = new [] { 1, 2, 3, 4 };
FunctionChangingByReference(ref ints[1]);
}
public void FunctionChangingByReference(ref int x)
{
x = 500;
}
In C# there are no pointers, only references.
(I'm lying a bit, you could use pointers if you create a unsafe context, but we don't do that in C#, and neither should you. When we code C++ we do, but that's C++, and we do it at a cost, we make the code a bit more fragile and error prone. When I code C# I try to optimize the code on a higher level than memory address shuffling. If you really need to optimize on that level you should write the code in C++ and import that code as a dll, then you have a good separation of concern, and don't forget to test drive the development!)
Simply myArray[1] = 500! You could use a property as Nahum Litvin has suggested if you specifically want a reference to a specific integer within the array.
#des answer has awaken my interest. So I tried his solution and it works as expected:
int[] numbers = new[] { 1, 2, 3 };
fixed (int* number = &numbers[0])
{
*number = 10;
}
Console.WriteLine(String.Join(", ", numbers)); // Outputs "10, 2, 3"
You have to compile it with the /unsafe option.
I hope you see that this may bring some problems.
Therefore I don't recommend this solution.
What you want is a basically pointer to a variable.
It's hard to explain the difference between "value type" (like int or struct), a reference and a pointer. I can only recommend learning C.
Here's solution that works, although it may need a lot of changes to your code.
//a class that will hold an int inside
public class myIntWrapper
{
//this is the value wrapper holds
public int theValue;
//constructor taking the value
public myIntWrapper(int argument)
{
theValue = argument;
}
//operator to convert an int into brand-new myIntWrapper class
public static implicit operator myIntWrapper(int argument)
{
return new myIntWrapper(argument);
}
//operator to convert a myIntWrapper class into an int
public static implicit operator int(myIntWrapper wrapper)
{
return wrapper.theValue;
}
}
now you can write:
//create an array -
//setting values to every item in array works
//thanks to operator myIntWrapper(int argument)
myIntWrapper[] myArray = new myIntWrapper[5]{1,2,3,4,5};
//now take a "reference"
myIntWrapper rr = myArray[1];
//change the value
rr.theValue = 500;
//from now on myArray[1].theValue is 500;
//thanks to operator int(myIntWrapper wrapper)
//you can write:
int ss = rr;//it works!
please remember to never do:
rr = 600;
because this will actually create brand new myIntWrapper, that's not "connected" anywhere.
So remember:
rr.theValue = 500;//this changes the value somewhere
rr = myArray[3];//this changes where rr is "pointing" to
Yes, it's quite complicated but I doubt it can be done any simpler without unsafe code. I'm sorry for not explaining it more. I'll answer to all questions in comments.
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.