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;
Related
I am trying to serialize a 2D array of strings to send over a network.
I have a couple ideas but I seem to be blocked by api or efficiency. I really don't want to serialize and send a small 2D array across a network 10 times a second when I only need it when I collide with something and ask for the information.
I have a list of items a player has in his cart. I want to make it okay to steal from that cart, so the other player has to be able to see what's in the cart.
If I use onphotonserializeview I thought I could convert the flatten the 2d array of strings into a 1D array of strings with a delimiter and stream.sendnext the delimited line and reconstitute on the other side, or some variation of that functionality.
public void serialize_merchandise_list() {
int width = 0;
int length = 0;
while((width < cart_list_width) && (length < cart_list_width)) {
width++;
if (width >= cart_list_width) {
length++;
width = 0;
}
// convert to bytes and then to string and append
System.Text.ASCIIEncoding.ASCII.GetBytes(merchandise_items[width, length]));
}
}
But doing that unnecessarily is an operation time nightmare, I want it on demand.
I was going to do a punrpc but I can't get a return value or ref parameter. What is the best approach here?
I want:
collide with cart
do you want to steal from cart --> yes --> show list
else --> go on your way
Afaik a one-dimensional array of strings would be no problem for Photon.
So I guess you could just flatten the array and additionally send the second size - lets name it element size (in contrary to the first one - the element count) as parameters.
Like e.g. lets say your array looks like
string[,] array2D = new string[4,2]{{"a", "b"}, {"c","d"}, {"e","f"}, {"g","h"}};
Then you would know the second (elementSize) dimension using either hardcoded or GetLength(int)
var elementSize = array2D.GetLength(1);
and you would simply flatten the array using
string[] arrayFlat = array2D.Cast<string>().ToArray();
So you would send these two informations via photon (I don't know photon in detail) but afaik you can do this one time without having to send it continiously. The two parameters would be
int elementSize, string[] arrayFlat
in this case with the values
2, string[8]{"a","b","c","d","e","f","g","h"}
So together:
public void SendArray2D()
{
int elementSize = array2D.GetLength(1);
string[] arrayFlat = array2D.Cast<string>().ToArray();
photonView.RPC(nameof(ReceiveArray2D), RpcTarget.All, elementSize, (object) arrayFlat);
}
Then on the receiver part you are getting
int elementSize = 2, string[] arrayFlat = string[8]{"a","b","c","d","e","f","g","h"}
and have to convert it back. You already know the second dimension elementSize = 2 so in order to get also the first one you simply do
var elementCount = arrayFlat.Length / elementSize; // = 4
so you know the 2D array you will have to fill would be
string[,] array2D = new string[elementCount, elementSize]; // = 4,2
and then you can simply iterate it and do something like
for (var x = 0; x < elementCount; x++)
{
for (int y = 0; y < elementSize; y++)
{
array2D[x, y] = arrayFlat[x * elementSize + y];
}
}
So together
[PunRpc]
public void ReceiveArray2D(int elementSize, string[] arrayFlat)
{
var elementCount = arrayFlat.Length / elementSize;
array2D = new string[elementCount, elementSize];
for (var x = 0; x < elementCount; x++)
{
for (int y = 0; y < elementSize; y++)
{
array2D[x, y] = arrayFlat[x * elementSize + y];
}
}
// Could also e.g. call some event like
OnReceivedArray2D?.Invoke (array2D);
}
public event Action<string[,]> OnReceivedArray2D;
So you could attach listeners to the event via
reference.OnReceivedArray2D += SomeHandler;
private void SomeHandler(string [,])
{
// ...
}
Or alternatively you could implement a class that stores your data in a flat array but lets you access it like if it would be a 2D array (in general a 2D array is anyway stored in memory as a flat one)
[Serializable]
public class SerializableArray2D
{
public readonly string[] ArrayFlat;
public readonly ElementSize;
public SerializableArray2D(int elementSize, string[] arrayFlat)
{
ElementSize = elementSize;
ArrayFlat = arrayFlat;
}
public SerializableArray2D(string[,] array2D) : this(array2D.GetLength(1), array2D.Cast<string>().ToArray()) { }
public SerializableArray2D(int elementSize, int elementCount) : this(elementSize, new string[elementCount]){}
public string this[int x, int y]
{
get { return ArrayFlat[x * ElementSize + y]; }
set { ArrayFlat[x * ElementSize + y] = value; }
}
}
usage would be e.g. Initialize it
var array2D = new string[4,2]{{"a", "b"}, {"c","d"}, {"e","f"}, {"g","h"}};
var serializableArray2D = new SerializableArray2D(array2D);
For accessing specific indices like in a 2D array
string example = serializableArray2D[1,1];
serializableArray2D[3,0] = "example";
For sending it
photonView.RPC(nameof(Receive), RpcTarget.All, serializableArray2D.ElementSize, (object)serializableArray2D.ArrayFlat);
And when receiving it
[PunRPC]
public void Receive(int elementSize, string[] arrayFlat)
{
serializableArray2D = new SerializableArray2D(elementSize, arrayFlat);
// Again e.g. call an event
OnReceivedSerializableArray2D?.Invoke(serializableArray2D);
}
public event Action<SerializableArray2D> OnReceivedSerializableArray2D;
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;
}
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
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()
I am trying to access an object which is a member of an object array, is that possible ?
I have declared a structure named Particle, and initialized an object array of "Particle" about 40 particles,now I need to access each particle, for ex: particle.Gbest
any one can help ??
here is my code:
struct particle
{
double[] position = new double[100];
double Gbest, Lbest;
double Pconst = 0.5;
}
object[] swarm = new object[swarm_size];
for (int i = 0; i < swarm_size; i++)
{
swarm[i] = new particle();
}
This code is invalid to start with:
struct particle
{
double[] position = new double[100];
}
You can't specify variable initializers for instance variables in structs.
However, accessing data within another object or value is easy - if it's accessible. In this case your fields are private and you haven't provided any access methods or properties, so you won't be able to get at them "from the outside" without more code.
Here's some modified code:
public class Particle
{
private readonly double[] positions = new double[100];
// TODO: Rename these to something useful
public double Gbest { get; private set; }
private double Lbest;
private double Pconst = 0.5;
public Particle(int g)
{
Gbest = g; // Or whatever
}
}
List<Particle> swarm = new List<Particle>();
for (int i = 0; i < swarmSize; i++)
{
swarm.Add(new Particle(i));
}
double total = 0;
foreach (Particle particle in swarm)
{
total += particle.Gbest;
}
Now this isn't doing anything particularly useful, because it's not clear what you're trying to do - but I would suggest you get an introductory book on C#. Stack Overflow is great for specific questions, but I think you're early enough on your journey into C# that a good book or tutorial would help you more.
var swarm = new particle[swarm_size];
for (int i = 0; i < swarm_size; i++)
{
swarm[i] = new particle();
}
To access properties of the array you could walk it like this:
for (int i = 0; i < swarm_size; i++)
{
Console.WriteLine(swarm[i].Gbest);
Console.WriteLine(swarm[i].Lbest);
}
EDIT: swarm declaration was incorrect.
If you know the index of the particle, you can simply use:
particle part = (particle)swarm[i];
// do stuff with part
If you don't know the index, you can iterate over them:
foreach( particle part in swam )
{
// do stuff with part
}
Or you can use Linq-to-Objects to do more sophisticated stuff, but I think you need to start with the simple stuff...
By the way, if you can, declare swarm as:
particle[] swarm = new particle[swarm_size];
and get the benefit of type-safety.
struct particle
{
public double[] position = new double[100];
public double Gbest, Lbest;
public double Pconst = 0.5;
}
particle[] swarm = new particle[swarm_size];
for (int i = 0; i < swarm_size; i++)
{
swarm[i] = new particle();
}
foreach(particle p in swarm)
{
// do magic here
}
You can either change your array type to Particle and access it with index like...
particle[] swarm = new particle[swarm_size];
particle firstParticle = swarm[0];
or you can cast the object back to a particle...
particle firstParticle = (particle)swarm[0];
I would recommend the first option thou for performance reasons.