Why does one extension mutate but the other doesn't - c#

I'm playing around with C# extensions and am confused about mutate methods.
Below are two code samples that are supposed to change the calling List. The first (shuffle) works but the second (CustomExtension) leaves the List unchanged.
At the end of the CustomExtension call the list parameter looks to be changed, but on returning from the method the List looks untouched.
Why does one work and the other doesn't? What's going on?
This works.
readonly private static Random rng = new Random();
public static void Shuffle<T>(this IList<T> list)
{
int n = list.Count;
while (n < 1)
{
n--;
int k = rng.Next(n + 1);
T value = list[k];
list[k] = list[n];
list[n] = value;
}
}
myList.Shuffle();
(The above was ripped from Randomize a List<T>)
This Doesn't
static void CustomExtension(this IList<int> list)
{
list = new List<int>() { 9, 10, 11 };
}
foo.CustomExtension();

In the first example, you're overwriting elements inside the List<T>. This mutates the list in-place.
In the second example you're creating a new list and replacing the local reference list which is a parameter which is not being passed using ref, so it has no effect outside of the scope of the method. Your foo instance is not being mutated in CustomExtension.
Extension methods in C# cannot pass the this parameter using ref so you cannot overwrite the caller's instance reference.
If you change the method and call-site to a normal method with a ref parameter, then it works as expected (but it still isn't mutating foo, it's replacing it):
static void CustomExtension(ref IList<int> list)
{
list = new List<int>() { 9, 10, 11 };
}
CustomExtension( ref foo );

Related

Why is an array passed without ref, changed by CopyTo inside the method?

As the array parameter is passed without ref-keyword, this code outputs initial value of array (i.e. 1...6):
using System;
using System.Linq;
class Program
{
static void Main(string[] args)
{
var arr = new int[] {1, 2, 3, 4, 5, 6};
Rotate(arr, 3);
Console.WriteLine(string.Join(',', arr));
}
static void Rotate(int[] nums, int k)
{
var tail = nums.TakeLast(k).ToArray();
nums = tail.Concat(nums)
.Take(nums.Length)
.ToArray();
}
}
It is obvious, because inside the Rotate method there is it's own array with values, copied from the parameter values. And if I want to change arr values in the calling method, I need to pass it to the Rotate method by ref, it works.
But I don't understand, why if replace assignment with CopyTo() method the parameter behaves as like it is passed by reference:
static void Rotate(int[] nums, int k)
{
var tail = nums.TakeLast(k).ToArray();
tail.Concat(nums)
.Take(nums.Length)
.ToArray()
.CopyTo(nums, 0);
}
Here's a go at explaining what happens, graphically. Your 3 approaches (regular, CopyTo, and ref) are very different..
I've sketched in the relative complexity of the swapping algo but mostly you can ignore it. The most important point about all this is around what happens when you use ref, and what happens when you manipulate the contents of a reference type that is being passed around. Contents manipulation is very different to swapping the whole thing out for something else
Click to expand..
Because CopyTo() sets the elements inside the array and does not reassign the array variable itself.
.ToArray() creates a new object while CopyTo() modifies it. The latter gets passed a reference to an existing array.
See also Passing Arrays by Value and by Reference and many others.

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

C#: Immutable class

I have a class which should be immutable in this class i have only get indexer a private set property so why this is not immutable and i can set some field in array as you could see in main class...
class ImmutableMatice
{
public decimal[,] Array { get; private set; } // immutable Property
public ImmutableMatice(decimal[,] array)
{
Array = array;
}
public decimal this[int index1, int index2]
{
get { return Array[index1, index2]; }
}
........
and in main method if i fill this class with data and change the data
static void Main(string[] args)
{
decimal[,] testData = new[,] {{1m, 2m}, {3m, 4m}};
ImmutableMatice matrix = new ImmutableMatice(testData);
Console.WriteLine(matrix[0,0]); // writes 1
testData[0, 0] = 999;
Console.WriteLine(matrix[0,0]); // writes 999 but i thought it should
// write 1 because class should be immutable?
}
}
Is there any way how to make this class immutable?
Ah yes the solution was copy array to new array in constructor like this:
public ImmutableMatice(decimal[,] array)
{
decimal[,] _array = new decimal[array.GetLength(0),array.GetLength(1)];
//var _array = new decimal[,] { };
for (int i = 0; i < array.GetLength(0); i++)
{
for (int j = 0; j < array.GetLength(1); j++)
{
_array[i, j] = array[i, j];
}
}
Array = _array;
}
That is because you are actually changing the data in the ARRAY, rather than the indexer.
static void Main(string[] args)
{
decimal[,] testData = new[,] {{1m, 2m}, {3m, 4m}};
ImmutableMatice matrix = new ImmutableMatice(testData);
Console.WriteLine(matrix[0,0]); // writes 1
testData[0, 0] = 999; // <--- THATS YOUR PROBLEM
Console.WriteLine(matrix[0,0]); // writes 999 but i thought it should
// write 1 because class should be immutable?
}
You can copy the array into your private property in the constructor to avoid this situation.
Note that you indeed cannot write matrix[0,0] = 999; because the indexer has no setter.
Edit
As Chris pointed out (how could I have missed it myself?) - you shouldn't expose the array as a property at all (which means in most cases it doesn't even have to be a property).
Consider the following code instead:
private decimal[,] _myArray; // That's private stuff - can't go wrong there.
public decimal this[int index1, int index2]
{
// If you only want to allow get data from the array, thats all you ever need
get { return Array[index1, index2]; }
}
Your class is immutable, but the objects inside it aren't.
Having public decimal[,] Array { get; private set; } will only guarantee that you cannot set the property Array to a new instance of Array, but it does not prevent you from accessing the existing object and changing its values (which aren't immutable).
You might want to look into the appropriately named ReadOnlyCollection<T> class.
As #Mike pointed out and I looked past the first time: there's a twist to this because you are accessing the value through the testData object and not through matrix. While the original point still stands, it is more exact to say that the problem you have is that you are changing values in the underlying object which has its reference passed around. You're bypassing the ImmutableMatice object alltogether.
The beforementioned solution of using a ReadOnlyCollection<T> still stands: by creating this read-only wrapper around it, you won't be able to change it anymore afterwards. Howver this is only the case when you actually use it the way its intended: through ImmutableMatice and not through the underlying collection which you still have a reference to.
Another solution that solves this problem is to copy the contents of the original array to another one to "disconnect" it from the array your still have a reference to.
In order to illustrate this, consider the following samples. The first one demonstrates how the underlying reference can still be influenced while the second one shows how it can be solved by copying your values to a new array.
void Main()
{
var arr = new[] { 5 };
var coll = new ReadOnlyCollection<int>(arr);
Console.WriteLine (coll[0]); // 5
arr[0] = 1;
Console.WriteLine (coll[0]); // 1
}
void Main()
{
var arr = new[] { 5 };
var arr2 = new int[] { 0 };
Array.Copy(arr, arr2, arr.Length);
var coll = new ReadOnlyCollection<int>(arr2);
Console.WriteLine (coll[0]); // 5
arr[0] = 1;
Console.WriteLine (coll[0]); // 5
}

Task parameters, do reference types point to same memory address on the heap

As far as i had understood, when you use a reference type as a parameter in a method, the value on the stack, is copied and the formal parameter therefore points to the same memory address, on the heap, as the original, hence changes are persisted once you have finished with the method.
How does this work with tasks? I have just created 2 new tasks and passed in an array which was declared on the UI thread. Changes made in one of the new tasks were immediately shown in the second task. When i try to change the input(the array) via the UI thread, the same parameter hasnt changed on the 2 new tasks. I was under the impression it should have since they should all be pointing at the same memory location on the heap??
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Threading;
namespace TasksAndMemory
{
class Program
{
private static ManualResetEvent mre = new ManualResetEvent(false);
static void Main(string[] args)
{
int[] readToEnd = new int[2];
int[] data = new int[] { 1, 2, 3, 4, 5, 6 };
int valueType = 5;
int pageCounter = 1;
Task[] tasks = new Task[2];
for (int x = 1; x < 3; x++)
{
//Needed due to closure problem
int i = x;
tasks[i-1] = Task.Factory.StartNew(() =>
{
SpecialMethod(data, readToEnd, i, valueType);
});
}
while(pageCounter < 4)
{
if (readToEnd[0] == 1 && readToEnd[1] == 1)
{
//Sets the state of the event to nonsignaled, causing threads to block
mre.Reset();
int[] temp = new int[] { 7, 8, 9, 10, 11, 12 };
data = temp;
readToEnd[0] = 0;
readToEnd[1] = 0;
//Sets the state of the event to signaled, allowing one or more waiting threads to proceed.
mre.Set();
pageCounter++;
}
}
Console.ReadLine();
}
public static void SpecialMethod(int[] historicalData, int[] readToEnd, int taskNumber, int valueTy)
{
int[] temp = new int[] { 100, 200, 300, 400, 500, 600 };
for (int x = 0; x <= historicalData.Length; x++)
{
if (x == historicalData.Length)
{
readToEnd[taskNumber-1] = 1;
mre.WaitOne();
x = 0;
}
else
{
valueTy++;
temp[x] = temp[x] + taskNumber;
}
}
}
}
}
You create an array:
int[] data = new int[] { 1, 2, 3, 4, 5, 6 };
You then pass a copy of your reference to this array (currently stored in data) as a parameter to SpecialMethod (via a capture in your original code, but not important, it's still just a copy).
Within SpecialMethod, the parameter int[] historicalData will receive a copy of the reference to this original array.
Thereafter, anything that causes the variable data to be reassigned (as opposed to changes made to data within the array referenced by data) has no effect on any copies that were made of its original reference - they're still referencing the original array.
I'm not clear on what your actual requirements are in terms of passing data between threads, so I cannot come up with any firm recommendations. I would usually try to avoid using raw arrays though.
Your analysis seems correct at the start, but your conclusions are not.
You have a reference type (an array) and you pass it to a method by value (which is the default). This means that the reference to that array, which sits on the heap, is copied.
Because both the variable in SpecialMethod and Main have the same reference, changing the value that they reference will be "seen" by both variables.
That only applies if you mutate the array. That's what you do with readToEnd, which is why the sections of code dealing with it work as you intended.
With data on the other hand you don't mutate the array, you just assign a new array to the variable. That's changing the reference, not the object that it references, and that's why you're having problems.
As for solutions, there are several. First, you could change the code to mutate the array rather than assigning a new one; just change the exising values. If you need to change the number of elements consider using a List rather than an array.
Another option is, rather than passing an array, is to add another layer of indirection. You could make a new class that has a property which is an array, pass an object of that type to SpecialMethod, and then you can change that object's property and see it reflected in both locations. You could use something like this to cover the general case:
public class Wrapper<T>
{
public T Value { get; set; }
}

Swap List<> elements with c# using LINQ

I have this list
var list = new List { 3, 1, 0, 5 };
I want to swap element 0 with 2
output
0, 1, 3, 5
If you just want it sorted, I'd use List.Sort().
If you want to swap, there is no built in method to do this. It'd be easy to write an extension method, though:
static void Swap<T>(this List<T> list, int index1, int index2)
{
T temp = list[index1];
list[index1] = list[index2];
list[index2] = temp;
}
You could then do:
list.Swap(0,2);
Classic swap is...
int temp = list[0];
list[0] = list[2];
list[2] = temp;
I don't think Linq has any 'swap' functionality if that's what you're looking for.
In the case that something is not directly supported ...make it so number 1!
Take a look at the concept of "extension methods". With this you can easily make your list support the concept of Swap() (this applies to any time you want to extend the functionality of a class).
namespace ExtensionMethods
{
//static class
public static class MyExtensions
{
//static method with the first parameter being the object you are extending
//the return type being the type you are extending
public static List<int> Swap(this List<int> list,
int firstIndex,
int secondIndex)
{
int temp = list[firstIndex];
list[firstIndex] = list[secondIndex];
list[secondIndex] = temp;
return list;
}
}
}

Categories

Resources