Get struct item by index in list and array - c#

When I use an array of structs (for example System.Drawing.Point), I can get item by index and change it.
For example (This code work fine):
Point[] points = new Point[] { new Point(0,0), new Point(1,1), new Point(2,2) };
for (int i = 0; i < points.Length; i++)
{
points[i].X += 1;
}
but when i use List it's not work:
Cannot modify the return value of
'System.Collections.Generic.List.this[int]'
because it is not a variable
Example(This code didn't work fine):
List<Point> points = new List<Point> { new Point(0,0), new Point(1,1), new Point(2,2) };
for (int i = 0; i < points.Count; i++)
{
points[i].X += 1;
}
I know that when I get list item by index I got copy of it and compiler hints that I did not commit an error, but why take the elements of the array index works differently?

This is because for the array points[i] shows where the object is located. In other words, basically points[i] for an array is a pointer in memory. You thus perform operations on-the-record in memory, not on some copy.
This is not the case for List<T>: it uses an array internally, but communicates through methods resulting in the fact that these methods will copy the values out of the internal array, and evidently modifying these copies does not make much sense: you immediately forget about them since you do not write the copy back to the internal array.
A way to solve this problem is, as the compiler suggests:
(3,11): error CS1612: Cannot modify a value type return value of `System.Collections.Generic.List<Point>.this[int]'. Consider storing the value in a temporary variable
So you could use the following "trick":
List<Point> points = new List<Point> { new Point(0,0), new Point(1,1), new Point(2,2) };
for (int i = 0; i < points.Count; i++) {
Point p = points[i];
p.X += 1;
points[i] = p;
}
So you read the copy into a temporary variable, modify the copy, and write it back to the List<T>.

The reason is that structs are value types so when you access a list element you will in fact access an intermediate copy of the element which has been returned by the indexer of the list.
Other words, when using the List<T>, you're are creating copies.
MSDN
Error Message
Cannot modify the return value of 'expression' because it is not a
variable
An attempt was made to modify a value type that was the result of an
intermediate expression. Because the value is not persisted, the value
will be unchanged.
To resolve this error, store the result of the expression in an
intermediate value, or use a reference type for the intermediate
expression.
Solution:
List<Point> points = new List<Point> { new Point(0,0), new Point(1,1), new Point(2,2) };
for (int i = 0; i < points.Count; i++)
{
Point p = points[i];
p.X += 1;
//and don't forget update the old value, because we're using copy
points[i] = p;
}

Related

Problems with array declaration without set size

In Microsoft Visual Studio (Community Edition of course) I have a simple C# program where I declare a simple array without a set size like so
int[] paths;
In a later part of the program I try to set parts of the array but it gives me an error
Use of unsigned local variable 'paths'
in this part of the program
for (int b = 0; b < 100; b++) {
paths[b] = b;
}
In conclusion, I suppose I'm wondering whether this problem that affects just me, or a problem of syntax. (I originated from Java which is half to blame how I ran into this question) I am willing to accept closure on whether this is possible or not,(If so source please) or If I'm just doing it wrong.(If so please provide a solution/suggestions in comments)
In C# Arrays are fixed size, and need to be initialized
var size = 100;
int[] paths = new int[size];
for (var b = 0; b < size; b++) {
paths[b] = b;
}
List is also another good thing for a C# beginner to know about, the difference between an array and a list, is that a List will resize itself for you automagically (I believe there is a very similar List class in Java)
var size = 100;
var paths = new List<int>();
for (var b = 0; b < size; b++) {
paths.Add(b);
}
You just declared the array, but you also need to create it before using it:
paths = new int[100];
You must follow the array declaration syntax.
int[] paths; //-> here you are declaring the paths array and your not creating array.
in memory it set paths array without its sizes. So you must set the size by creating this array as follows.
int[] paths = new int[10];
you can create and declare array like as follows, for to assign another array with it.
EG:
int[] arr1 = new int[] { 0,1,2,3,4,5,6,7};
int[] paths =arr1;
here arr1 size and data will be automatically assigned to paths array.
Firstly you are creating a variable but you have not assigned it at all, this is the reason for you error.
Secondly in C# arrays require a fixed size and can not be resized so when initializing a size must be provided, either explicitly as below or implicitly by initializing it with objects (see comments).
int[] paths; // declaration
paths = new int[100] // assignment, array initialized with a size of 100
// with objects
// paths = new int[] { 1, 2, 3 };
// declaration and assignment can also be merged into 1 line.
// i.e int[] paths = new int[100]
for (int b = 0; b < 100; b++) {
paths[b] = b;
}
you must declare and initialize the array element in your code like
int[] myInt = new int[100]
If you don't know the size before initialize you need to use List like
List<int> list = new List<int>();
list.Add(123);
list.Add(456);
list.Add(789);
Console.WriteLine(list[2]);
and it is necessary to use array and size of array is not fixed before initialize you need to follow below code but below code is wastage of memory usage and more execution process. so my recommend is use List instead of int[]
// Global Variable
int[] myInt;
int mySize = 1;
private void button1_Click(object sender, EventArgs e)
{
if (mySize > 1)
{
int[] tempInt = new int[mySize];
tempInt = myInt;
myInt = new int[mySize];
for (int i = 0; i < mySize - 1; i++)
{
myInt[i] = tempInt[i];
}
myInt[mySize - 1] = mySize;
}
else
{
myInt = new int[mySize];
myInt[mySize - 1] = mySize;
}
mySize++;
}

shallow-copy a segment of a value type array

I'm trying to shallow-copy a double[] into segments, and pass those segments to new threads, like so:
for (int i = 0; i < threadsArray.Length; i++)
{
sub[i] = new double[4];
//Doesn't shallow copy since double is a value type
Array.Copy(full, i * 4, sub[i], 0, 4);
double[] tempSub = sub[i];
threadsArray[i] = new Thread(() => DoStuff(tempSub));
threadsArray[i].Start();
}
What would be the best way to have the created segments reference the original array?
You could use the ArraySegment<T> structure:
for (int i = 0; i < threadsArray.Length; i++)
{
sub[i] = new ArraySegment<double>(full, i * 4, 4);
ArraySegment<double> tempSub = sub[i];
threadsArray[i] = new Thread(() => DoStuff(tempSub));
threadsArray[i].Start();
}
(note that you will need to change the signature of DoStuff to accept an ArraySegment<double> (or at least an IList<double> instead of an array)
ArraySegment<T> doesn't copy the data; it just represents an segment of the array, by keeping a reference to the original array, an offset and a count. Since .NET 4.5 it implements IList<T>, which means you can use it without worrying about manipulating the offset and count.
In pre-.NET 4.5, you can create an extension method to allow easy enumeration of the ArraySegment<T>:
public static IEnumerable<T> AsEnumerable<T>(this ArraySegment<T> segment)
{
for (int i = 0; i < segment.Count; i++)
{
yield return segment.Array[segment.Offset + i];
}
}
...
ArraySegment<double> segment = ...
foreach (double d in segment.AsEnumerable())
{
...
}
If you need to access the items by index, you will need to create a wrapper that implements IList<T>. Here's a simple implementation: http://pastebin.com/cRcpBemQ

C# three dimensional array, convert row to array

var examples = new double[1000][][];
for (int i = 0; i < 1000; i++)
{
examples[i]= new double[2][]; //the Set
examples[i][0] = new double[] { x(i), y(i) }; //the Inputs
examples[i][1] = new double[] { Math.Sin(x(i) * y(i)), Math.Sin(x(i) / y(i)) }; //the Target
}
x(i) means x depend on i
And I need to collect the inputs like that examples[][0] = new double[1000][]
because It will consume more memory to create a new array like that
for (int i = 0; i < 1000; i++)
{
input[i][0]=x(i);
input[i][1]=y(i);
}
My first thought would be to reconsider the design decision that ended up with a [1000][2][2] array which you want to pull a flattened [1000][2] array out of by taking a specific center-array value...
As far as accessing it without re-allocating memory, you're not going to get it as an array without using more memory sorry, but what you could do is get an IEnumerable<double[]> which may suffice depending on your purposes by using yield (Which will make it evaluate each result individually as you enumerate over it).
EG:
public IEnumerable<double[]> GetResults()
{
for (int i = 0; i < 1000; x++)
{
yield return examples[i][0];
}
}
How useful that is will depend on how you plan to use the results, but you won't get a new array without using more memory.

How to create array of 100 new objects?

I'm trying something like that:
class point
{
public int x;
public int y;
}
point[] array = new point[100];
array[0].x = 5;
and here's the error:
Object reference not set to an instance of an object. (# the last line)
whats wrong? :P
It only creates the array, but all elements are initialized with null.
You need a loop or something similar to create instances of your class.
(foreach loops dont work in this case)
Example:
point[] array = new point[100];
for(int i = 0; i < 100; ++i)
{
array[i] = new point();
}
array[0].x = 5;
When you do
point[] array = new point[100];
you create an array, not 100 objects. Elements of the array are null. At that point you have to create each element:
array[0] = new point();
array[0].x = 5;
You can change class point to struct point in that case new point[500] will create an array of points initialized to 0,0 (rather than array of null's).
As the other answers explain, you need to initialize the objects at each array location. You can use a method such as the following to create pre-initialized arrays
T[] CreateInitializedArray<T>(int size) where T : new()
{
var arr = new T[size];
for (int i = 0; i < size; i++)
arr[i] = new T();
return arr;
}
If your class doesn't have a parameterless constructor you could use something like:
T[] CreateInitializedArray<T>(int size, Func<T> factory)
{
var arr = new T[size];
for (int i = 0; i < size; i++)
arr[i] = factory();
return arr;
}
LINQ versions for both methods are trivial, but slightly less efficient I believe
int[] asd = new int[99];
for (int i = 0; i < 100; i++)
asd[i] = i;
Something like that?
Sometimes LINQ comes in handy. It may provide some extra readability and reduce boilerplate and repetition. The downside is that extra allocations are required: enumerators are created and ToArray does not know the array size beforehand, so it might need to reallocate the internal buffer several times. Use only in code whose maintainability is much more critical than its performance.
using System.Linq;
const int pointsCount = 100;
point[] array = Enumerable.Range(0, pointsCount)
.Select(_ => new point())
.ToArray()

Directly modifying List<T> elements

I have this struct:
struct Map
{
public int Size;
public Map ( int size )
{
this.Size = size;
}
public override string ToString ( )
{
return String.Format ( "Size: {0}", this.Size );
}
}
When using array, it works:
Map [ ] arr = new Map [ 4 ] {
new Map(10),
new Map(20),
new Map(30),
new Map(40)};
arr [ 2 ].Size = 0;
But when using List, it doesn't compile:
List<Map> list = new List<Map> ( ) {
new Map(10),
new Map(20),
new Map(30),
new Map(40)};
list [ 2 ].Size = 0;
Why?
The C# compiler will give you the following error:
Cannot modify the return value of 'System.Collections.Generic.List.this[int]' because it is not a variable
The reason is that structs are value types so when you access a list element you will in fact access an intermediate copy of the element which has been returned by the indexer of the list.
From MSDN:
Error Message
Cannot modify the return value of
'expression' because it is not a
variable
An attempt was made to modify a value
type that was the result of an
intermediate expression. Because the
value is not persisted, the value will
be unchanged.
To resolve this error, store the
result of the expression in an
intermediate value, or use a reference
type for the intermediate expression.
Solutions:
Use an array. This gives you direct access to the elements (you are not accessing a copy)
When you make Map a class you can still use a List to store your element. You will then get a reference to a Map object instead of an intermediate copy and you will be able to modify the object.
If you cannot change Map from struct to a class you must save the list item in a temporary variable:
List<Map> list = new List<Map>() {
new Map(10),
new Map(20),
new Map(30),
new Map(40)
};
Map map = list[2];
map.Size = 42;
list[2] = map;
Because it is a struct, when using the List<T>, you're are creating copies.
When using a struct, it is better to make them immutable. This will avoids effects like this.
When using an array, you have direct access to the memory structures. Using the List<T>.get_Item you work on a return value, that is, a copy of the structure.
If it was a class, you would get a copy of a pointer to the class, but you would not notice this, since pointers are hidden in C#.
Also using the List<T>.ToArray does not solve it, because it will create a copy of the internal array, and return this.
And this is not the only place where you will get effects like this when using a struct.
The solution provided by Divo is a very good workaround. But you have to remember to work this way, not only when using the List<T> but everywhere you want to change a field inside the struct.
I'm not an XNA developer, so I cannot comment on how strict is the requirement to use structs vs. classes for XNA. But this does seem to be a much more natural place to use a class in order to get the pass-by-ref semantics you are looking for.
One thought I had is that you could make use of boxing. By creating an interface, say, 'IMap' in your case, to be implanted by your 'Map' struct, and then using a List<IMap>, the list will be holding System.Object objects, which are passed by reference. For example:
interface IMap
{
int Size { get; set; }
}
struct Map: IMap
{
public Map(int size)
{
_size = size;
}
private int _size;
public int Size
{
get { return _size; }
set { _size = value; }
}
public override string ToString()
{
return String.Format("Size: {0}", this.Size);
}
}
Which could then be called by the following:
List<IMap> list = new List<IMap>() {
new Map(10),
new Map(20),
new Map(30),
new Map(40)};
list[2].Size = 4;
Console.WriteLine("list[2].Size = " + list[2].Size.ToString());
Note that these structs would only be boxed once, when passed into the List in the first place, and NOT when called using code such as 'list[2].Size = 4', so it should be fairly efficient, unless you are taking these IMap objects and casting back to Map (copying it out of the List<IMap>) in other parts of your code.
Although this would achieve your goal of having direct read-write access to the structs within the List<>, boxing the struct is really stuffing the struct into a class (a System.Object) and I would, therefore, think that it might make more sense to make your 'Map' a class in the first place?
Mike
I keep coming back to this question when trying to calculate normals on a vertex buffer in XNA.
The best XNA solution I came up with was to copy the data (or store it) in an array.
private void SomeFunction()
{
List<VertexBasicTerrain> vertexList = GenerateVertices();
short[] indexArray = GenerateIndices();
CalculateNormals(vertexList, ref indexArray); // Will not work
var vertexArray = vertexList.ToArray();
CalculateNormals(ref vertexArray, ref indexArray);
}
// This works (algorithm from Reimers.net)
private void CalculateNormals(ref VertexBasicTerrain[] vertices, ref short[] indices)
{
for (int i = 0; i < vertices.Length; i++)
vertices[i].Normal = new Vector3(0, 0, 0);
for (int i = 0; i < indices.Length / 3; i++)
{
Vector3 firstvec = vertices[indices[i * 3 + 1]].Position - vertices[indices[i * 3]].Position;
Vector3 secondvec = vertices[indices[i * 3]].Position - vertices[indices[i * 3 + 2]].Position;
Vector3 normal = Vector3.Cross(firstvec, secondvec);
normal.Normalize();
vertices[indices[i * 3]].Normal += normal;
vertices[indices[i * 3 + 1]].Normal += normal;
vertices[indices[i * 3 + 2]].Normal += normal;
}
for (int i = 0; i < vertices.Length; i++)
vertices[i].Normal.Normalize();
}
// This does NOT work and throws a compiler error because of the List<T>
private void CalculateNormals(List<VertexBasicTerrain> vertices, ref short[] indices)
{
for (int i = 0; i < vertices.Length; i++)
vertices[i].Normal = new Vector3(0, 0, 0);
for (int i = 0; i < indices.Length / 3; i++)
{
Vector3 firstvec = vertices[indices[i * 3 + 1]].Position - vertices[indices[i * 3]].Position;
Vector3 secondvec = vertices[indices[i * 3]].Position - vertices[indices[i * 3 + 2]].Position;
Vector3 normal = Vector3.Cross(firstvec, secondvec);
normal.Normalize();
vertices[indices[i * 3]].Normal += normal;
vertices[indices[i * 3 + 1]].Normal += normal;
vertices[indices[i * 3 + 2]].Normal += normal;
}
for (int i = 0; i < vertices.Length; i++)
vertices[i].Normal.Normalize();
}
list [ 2 ].Size = 0;
is in fact:
//Copy of object
Map map = list[2];
map.Size = 2;
Use class in place of struct.
I decided to directly replace the result on a copy and reassign the result like:
Map map = arr [ 2 ];
map.Size = 0;
arr [ 2 ] = map;

Categories