C# 7.3+ ref variable intended behaviour? - c#

I just came across a situation that boils down to this:
private static uint[] intArray;
private static void Main(string[] args)
{
intArray = new uint[10];
for(var i = 0u; i < intArray.Length; i++)
{
intArray[i] = i;
}
ref var value = ref intArray[4];
Array.Resize(ref intArray, 100);
value += 10; // what are we modifying here? The old memory?
Console.WriteLine(intArray[4]); // prints 4 not 14 because the array is resized
}
Is this the intended behavior for ref variables in C#?

Yes, this is expected. Array.Resize() does not literally resize the array, it can't, an array is contiguous memory. What it does is create a new array with the required size and copy the values from the old array into it. As explained here: https://learn.microsoft.com/en-us/dotnet/api/system.array.resize?view=netframework-4.8
You also have the question in the comment: "what are we modifying here? The old memory?" Yes, although you have no way of accessing the old array, there is still your reference to the element within it, so the GC cannot delete it until value goes out of scope. So there is no error from updating the contents.
int is a value type, so the value is copied to the new array. If you had an array of Object the value of the reference would be copied and you could still access that through your variable value.

Related

Why changes is one array data changing the value of another array in C# [duplicate]

This question already has answers here:
Copy Arrays to Array
(7 answers)
Closed 3 years ago.
Why the arr[1] value is changing when changing the value of arr1[1]? and similarly why it is changing when ((int[])o)[1] = 1000;
class Program
{
static void Main(string[] args)
{
int[] arr = new int[2];
arr[1] = 10;
Object o = arr;
int[] arr1 = (int[])o;
arr1[1] = 100;
Console.WriteLine(arr[1]);
((int[])o)[1] = 1000;
Console.WriteLine(arr[1]);
}
}
Why the answer is 100 and 1000?
This code is question-5 from https://www.techbeamers.com/csharp-coding-interview-questions-developers/
Why the arr[1] value is changing when changing the value of arr1[1]?
This is your first question, but keep it now after answering the second.
and similarly why it is changing when ((int[])o)[1] = 1000;
This is your second question.
Answer is by seeing what is the initial value of o variable.
Object o = arr;
The previous line sets o to arr, but it's not copying the elements from arr to o. They're reference types, so, now both o and arr refers to the same memory block. Therefore, any changes made to arr will affect o and vice-versa. Because they're sharing the same memory.
Now, let's get back to your first question:
It's really the same answer as the second one.
int[] arr1 = (int[])o;
The previous line sets arr1 to o, but again, they just holds the same memory address.
To summarize:
You created arr
You created o and made its reference the same as arr, so they both share the same memory.
You created arr1 and made its reference the same as o which also same as arr. so, arr and arr1 and o, all have the same reference.
Any change to any of them will affect the others.
arr, arr1 and o point to the same base memory address of the same array.
In C# an array is used by reference.
Reference is a hidden managed pointer to forget to manage it.

How to deal with arrays inside a struct?

I'm looking for a way to replace some of my List objects with arrays in my project to boost performance. The reasoning is that the List I would replace do not change size often (sometimes only once), so it would make sense to swap them out with arrays. Also, I would like to prevent allocations on the heap as much as possible.
So my idea is to create a struct (ArrayStruct) with two members "Count" and "Array". The struct would have some functions to add/remove/get/set/ect... elements in the array. The count would keep count of the elements that are usable in the array, and the array would only increase in size, never decrease.
Note that this is for a very particular case because I know the array size won't be very big.
The main problem I have is when I pass the reference to an already existing ArrayStruct object (named a in the example) to another (named b in the example) - I can edit the array in it, but once I resize it in a, I get an out of range exception in b. Is this because of the way I'm updating the reference via System.Array.Resize, so that the object b somehow does not attribute this change?
The only thing I could come up with as an idea was trying to override the assignment operator so that it would create a new array on assignment... but obviously that does not work.
My ArrayStruct
public struct ArrayStruct<T>
{
[SerializeField] private int m_Count;
[SerializeField] private T[] m_Array;
public int Count => m_Count;
public void Initialize(int startSize)
{
m_Count = 0;
m_Array = new T[startSize];
}
public T GetAt(int index)
{
return m_Array[index];
}
public void SetAt(int index, T newValue)
{
m_Array[index] = newValue;
}
public void Add(T newElement)
{
if (m_Array == null) {
m_Array = new T[1];
} else if (m_Array.Length == m_Count) {
System.Array.Resize(ref m_Array, m_Count + 1);
}
m_Array[m_Count] = newElement;
m_Count++;
}
public void Remove(T element)
{
for (int i = 0; i < m_Count; ++i) {
if (!element.Equals(m_Array[i])) {
continue;
}
for (int j = index; j < m_Count - 1; ++j) {
m_Array[j] = m_Array[j + 1];
}
m_Count--;
m_Array[m_Count] = default(T);
break;
}
}
//Trying to overload the = operating by creating a auto cast, this gives a compile error
/*public static implicit operator ArrayStruct<T>(ArrayStruct<T> otherArray)
{
var newArray = new ArrayStruct<T>();
newArray.m_Count = otherArray.Count;
newArray.m_Array = new T[otherArray.Length];
otherArray.m_Array.CopyTo(newArray.m_Array);
return newArray;
}*/
An example showcasing the problem
var a = new ArrayStruct<string>()
a.Add("hello");
var b = a;
print (b.GetAt(0)); //"hello"
a.SetAt(0, "new value for a");
print(b.GetAt(0));//"new value for a" , changing a changed b because the array is the same in both, this is bad
a.Add("resizing array");
print (b.GetAt(1)); //"Out of range exception" , because the array was resized for a but not for b therefore the connection broke
So do any of you have an idea of how I could make a new copy the array when I assign the struct to another variable?
Of course, I know I could use a function to do something like so
b = new ArrayStruct(a);
But I want it to be implicit. Or if there was a way to prevent assignments that would also work for me.
var a = new ArrayStruct();//let this work
var b = a; //Prevent this from happening
If you have any other solutions for replacing Lists with arrays conveniently please let me know
So do any of you have an idea of how I could make a new copy the array when I assign the struct to another variable? Of course, I know I could use a function to do something like so b = new ArrayStruct(a); But I want it to be implicit.
You can't do that. If a is a struct and you execute var b = a, then it will always just set b to a shallow copy of a; there's no way to change that behavior.
Doing something like b = new ArrayStruct(a) is the best you can do.
You're not understanding what the "=" operator is doing there. Objects are defined by reference, meaning when you write var b = a; you are actually asking for what you wish to avoid to happen, that is - object "b", pointing to the same object, which is "a".
You're passing the object "b" to point to the reference of object "a", which object holds the address to the object in question you defined, using the new keyword var a = new ArrayStruct<string>()
Your operator overloading does not work because you can't use it for conversions of the same type as the class you're writing the operator in. You'd either have to make a separate object (class), which would defeat the point you're trying to fix, or pass the m_array instead: public static implicit operator ArrayStruct<T>(T[] array)
You can also do something like this:
public static ArrayStruct<T> CopyFrom (ArrayStruct<T> array)
{
var newArray = new ArrayStruct<T>();
newArray.m_Array = new T[array.Count + 1];
newArray.Count = array.Count;
Array.Copy(array.m_Array, 0, newArray.m_Array, 0, array.m_Array.Length);
return newArray;
}
Now it should work exactly as you wanted.
var a = new ArrayStruct<string>();
a.Add("hello");
var b = ArrayStruct<string>.CopyFrom(a);
a.SetAt(0, "new value for a");
a.Add("resizing array");
Console.WriteLine(b.Count); // b returns 1
Console.WriteLine(a.Count); // a returns 2
As to why your index was going out of bounds, i.e it did not preserve the reference found in "a", this is all in correlation with references, yet again.
In the following code System.Array.Resize(ref m_Array, Count + 1); you're changing the reference found for object m_array in "a", when you're adding the new element with the line a.Add("resizing array");. That essentially means that your "a" object's m_array (which is another object of type array[]) is now pointing to a new object (of type array[]), which is of a new size.
Okay, but object "a" is now using the new reference, whilst object "b" is still using the old one. m_array in object "b" only has a single element, while the new m_array object in "a" has the newly added element "resizing array", along with any previously added ones.
I believe that's how System.Array.Resize works? Edit me if I'm wrong.
Below is a resource explaining how to do a deep cloning of an object, if that's what you truly wish to do (i.e create a completely new copy of an object, as well as any other objects inside)
Deep cloning of objects

How to dynamically resize an array?

I have an array with Length = 3 and with values, for example {1,2,3}.
I need to dynamically resize it.
I know that I can use List or
Array.Resize(). But I need to know how to implement my own resize method?
You can try the following code. Create new array with new size and copy the old array data to the newly created array.
public static Array ResizeArray (Array oldArray, int newSize)
{
int oldSize = oldArray.Length;
Type elementType = oldArray.GetType().GetElementType();
Array newArray = Array.CreateInstance(elementType,newSize);
int preserveLength = System.Math.Min(oldSize,newSize);
if (preserveLength > 0)
{
Array.Copy (oldArray,newArray,preserveLength);
}
return newArray;
}
If this is just for practice then do it. but i suggest you use .Net Array.Resize() if you want to use it in your code. usually .Net libraries are best implemented and optimized.
Any way...you can do it with generic method
private static void Resize<T>(ref T[] array,int size)
{
T[] token = array.Take(size).ToArray(); // attempt to take first n elements
T[] temp = new T[size]; // create new reference
token.CopyTo(temp, 0); // copy array contents to new array
array = temp; // change reference
}
Note that you cannot do this without parameter ref. (The other way is to return array of course)
You may think arrays are passed by reference. thats true. But the references it self are passed by value. so whenever you try to change the reference it does not refer to the original array anymore. you can only change the contents. To make this possible you have to use ref to directly pass the reference into method.
About the code:
{1,2,3} if you resize it to 2 obviously it will remove the last parameter. so you will have {1,2}
if you resize it to 4 then it will give the array with new default values. either null for reference types or 0 for value types (false for Boolean type). here you will have {1,2,3,0}
int[] array = new[] {1, 2, 3};
Resize(ref array,4);
Console.WriteLine(array.Length); // outputs 4
Well, you may look on Array.Resize source code before making your own implementation =)
http://referencesource.microsoft.com/#mscorlib/system/array.cs,71074deaf111c4e3
If you want to resize the array use the build in method Array.Resize().It will be easier and faster.
If you need a dynamically sized data structure, use List for that.
You can always do array.ToList() -> fill the list and do .ToArray() later.
Make an array of the new desired size, copy all items over. Make the variable that referenced your old array reference the new one.
Code from MSDN:
public static void Resize<T>(ref T[] array, int newSize) {
if (newSize < 0) {
throw new ArgumentOutOfRangeException("newSize", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
Contract.Ensures(Contract.ValueAtReturn(out array) != null);
Contract.Ensures(Contract.ValueAtReturn(out array).Length == newSize);
Contract.EndContractBlock();
T[] larray = array;
if (larray == null) {
array = new T[newSize];
return;
}
if (larray.Length != newSize) {
T[] newArray = new T[newSize];
Array.Copy(larray, 0, newArray, 0, larray.Length > newSize? newSize : larray.Length);
array = newArray;
}
}
}

Array is affecting other array value C#

static int[] scores = new int[100];
static int[] scorescopy;
public static int orderscores()
{
scorescopy = scores;
Array.Sort(scorescopy);
int sortingtoolb = 0;
return 0;
}
I am trying to get a copy of my initial array and then trying to sort that copy. However, when I use the Array.Sort() function, my first array keeps on being sorted as well, but I would like to preserve it. I tried taking away the new declaration on the scorescopy and that did not effect the result.
Also, is there a way to keep my unused variables within the array as null? (if I am not using all parts of it, I get a bunch of 0's in the beginning of the array).
I am using Visual Studio Express 2012 for Windows 8 on a system running Windows 8.1 Pro.
An array, when assigned, only copies a reference to the same array in memory. You need to actually copy the values for this to work:
public static int orderscores()
{
scorescopy = scores.ToArray(); // Using LINQ to "cheat" and make the copy simple
Array.Sort(scorescopy);
int sortingtoolb = 0;
return 0;
}
Note that you can do this without LINQ via:
scorescopy = new int[scores.Length];
Array.Copy(scores, scorescopy, scores.Length);
//... rest of your code
The expression scorescopy = scores; duplicate the handle to the array.
if you want to create a copy of the array items you should change that line to:
scores.copyTo(scorescopy,0);
You still need to make sure scorecopy has enough room to store the items.
so you also need this expression: static int[] scorescopy = new int[scores.Length];
and now your code should like this:
static int[] scores = new int[100];
static int[] scorescopy = new int[scores.Length];
public static int orderscores()
{
scores.copyTo(scorescopy,0);
Array.Sort(scorescopy);
int sortingtoolb = 0;
return 0;
}
You are getting a pointer to the same array, you want a clone:
scorescopy = (int [])scores.Clone();

Recursion, C# with array of char after return

In the code below, when I examine the Chars variable while stepping through the code in the debugger, the size of the char array is 0 before the return line in the last iteration, but after the return line its 1 and continues growing back to the original size.
Why is this happening? thanks for any help in advance.
static void Main(string[] args)
{
string str = "Hello";
PrintReverse(str.ToArray()); // prints "olleH"
Console.Read();
}
static void PrintReverse(char[] Chars)
{
Console.Write(Chars[Chars.Length - 1]);
Array.Resize(ref Chars, Chars.Length - 1);
if (Chars.Length == 0) return;
PrintReverse(Chars);
}
You have two issues here.
First, the 'before/after return' issue means you are seeing two different execution frames-- that is, in the debugger the stack trace will show a bunch of PrintReverses on top of each other because each is there in its own context, with its own state, concurrently. It's almost (though not really) like an 'instance' of that method, and you're seeing two different ones.
Second, because each has its own state, the local variables in each-- including, critically, the parameters-- are also duplicated. They initially point to the same heap object (your initial Char array), but they are all different variables.
Now, look at this code:
char[] test1 = new char[] { '1', '2', '3' }, test2 = test1;
Array.Resize(ref test2, 2);
MessageBox.Show(new string(test1) + " - " + new string(test2)); // result: 123 - 12
If you run this, you'll see that although the variables initially refer to the same object, Array.Resize creates a new object and changes the reference of the variable passed in to point to the new one. The reference in the first variable remains pointing at the old (immutable) object.
This is what's happening in your case, only with the Chars parameter. In each method, you reassign Chars to point elsewhere using Array.Resize(), but the original variables remain referencing the old location.
Try adding ref to the parameter declaration, this should now work the way you expected it to.
Without ref the call to Array.Resize can only modify the local array reference and not the reference passed in from Main.
static void Main(string[] args)
{
string str = "Hello";
var array = str.ToArray();
PrintReverse(ref array);
Console.Read();
Debug.Assert(array.Length == 0);
}
static void PrintReverse(ref char[] Chars)
{
Console.Write(Chars[Chars.Length - 1]);
Array.Resize(ref Chars, Chars.Length - 1);
if (Chars.Length == 0) return;
PrintReverse(ref Chars);
}
Edit:
I was mistaken about ref causing a shallow clone, this is the proof:
static void Main(string[] args)
{
var array = new[] { new object() };
TestRef(ref array, array);
}
static void TestRef(ref object[] arrayByRef, object[] arrayByValue)
{
Debug.Assert(ReferenceEquals(arrayByRef, arrayByValue)); //no difference whether passed by ref or value, if there was a shallow clone happening, this would fail
Array.Resize(ref arrayByRef, 2);
Debug.Assert(!ReferenceEquals(arrayByRef, arrayByValue)); //only now do they differ
}
Think about the chain of execution. You are recursively calling the method that shrinks the array until you get to 0, then you return to the caller (the same method) so you are seeing the grow back to the original size as you traverse back up the call stack.
No extra logic is happening as a result of this, as the recursive call is the last call in the method, but you get to see the debugger end each call, which in turn had an array size 1 bigger than the call before it.
If you were to pass the array by reference instead, however, the array size would remain at size 0 as it came up the call stack because each successive call to Array.Resize would create a new array and update all of the references to the new array instead of only the reference local to that call. (where as if you don't pass by reference it updates only a copy of the reference, and does not update those in the calls before it).
This is because Array.Resize creates a new array and updates the reference to point to the new array instead of the old array, and by not passing by reference, you are sending a copy of the reference to the original array instead of the actual reference to the array, thus the calls to Array.Resize do not update the old references.
static void PrintReverse(ref char[] Chars)
{
Console.Write(Chars[Chars.Length - 1]);
Array.Resize(ref Chars, Chars.Length - 1);
if (Chars.Length == 0) return;
PrintReverse(ref Chars);
}
Thanks Groo for correcting me, hopefully I have it right this time

Categories

Resources