C
C#•2mo ago
olsu

List<> modification

I want ot modify all elements in a List<>, but im having trouble. This is my code. Which gives an error saying I cannot modify vertex because it is a 'foreach iteration variable'
public struct Vertex
{
public float life;
};

class Program
{
public static void Main(string[] args)
{
List<Vertex> vertices = new List<Vertex>();
vertices.Add(new Vertex());

foreach (var vertex in vertices)
{
vertex.life += 1;
}
}
}
public struct Vertex
{
public float life;
};

class Program
{
public static void Main(string[] args)
{
List<Vertex> vertices = new List<Vertex>();
vertices.Add(new Vertex());

foreach (var vertex in vertices)
{
vertex.life += 1;
}
}
}
I come from c++, so I can guess that the problem is that var vertex is copied and not actually what i want to modify. Is there a way to get the actual instance of the vertex in c#?
43 Replies
SpReeD
SpReeD•2mo ago
You are adding a new instance of Vertex into the List. The List has a length of 1 and can be accessed through a zero-based index -> vertices[0].
olsu
olsu•2mo ago
that does not work, it tells me it's not a variable (i assume because it's copying the value)?
SpReeD
SpReeD•2mo ago
In C# most things are referenced, they keyword ref is a double-reference. So all classes are referenced by default. Except structs, those are value-types. As you come from cpp you probably know the stack and heap stuff. vertices is a variable, scoped within a method, not the program class.
olsu
olsu•2mo ago
one minute, i got an isolated example working, but my actual use-case doesnt work
struct Test
{
float a;
};

class Test1
{
List<Test> list;

Test1()
{
list = new List<Test>();

list.Add(new Test());
list[0].a = 1;
}
}
struct Test
{
float a;
};

class Test1
{
List<Test> list;

Test1()
{
list = new List<Test>();

list.Add(new Test());
list[0].a = 1;
}
}
something like this does not work, but recreating list[0] with a new Test does-- is there a way to modify the existing instance without remaking it? is that what you meant earlier i guess? since using
class Test
{
public float a { get; set; }
};
class Test
{
public float a { get; set; }
};
fixes it
SpReeD
SpReeD•2mo ago
sure, access it through the index list[0]. public float a { get; set; } is a Property, not a variable and not a field, also this syntax is code-sugar to what it actually is, is way more code, this syntax is called auto-property, since getter and setter are auto generated and the backing/shadow field is also auto-generated. Properties are class/object scoped.
olsu
olsu•2mo ago
i didnt mean to have the get; set;, it doesnt actually need to be there
SpReeD
SpReeD•2mo ago
public float a; is a field
olsu
olsu•2mo ago
im a little confused what the distinction is between property, variable, and field is it synonymous to like a member variable?
SpReeD
SpReeD•2mo ago
but both are scoped class-wide and can be accessed on other methods, by using the public accessor it can also be accessed from outside. This isn't allowed for a field by code convetion, public should be for properties. No, big differences - also arguments and parameters
olsu
olsu•2mo ago
what would you suggest i use? the main problem im having is that im using these for vertex data sent to the gpu, and im honestly not knowledgeable enough to know if the layout of a class is acceptable for my shaders
SpReeD
SpReeD•2mo ago
Me neither^^ I don't know which framework you're using, etc.
olsu
olsu•2mo ago
im on fna, which uses hlsl for its shaders or actually it's using effects, but same difference i guess
SpReeD
SpReeD•2mo ago
However, if you wanna use the list inside your class and not only inside a single method, then, make it a private field 🙂 private List<Vertex> myVertices; and put a new instance creation in the ctor, or do it inline like: private List<Vertex> myVertices = []; fna? do you mean XNA?
olsu
olsu•2mo ago
fna is like a maintained wrapper on xna since microsoft discontinued xna
SpReeD
SpReeD•2mo ago
and peeps made MonoGame from it, never heard of fna tbh
olsu
olsu•2mo ago
mostly-- code is just copy-paste away going between the two with some exceptions yeah, they're all pretty similar i'd say anything that flies in xna flies in fna most of the time
SpReeD
SpReeD•2mo ago
Okay, I don't know about fna. but for monogame there exists nuget-packages that handles animations, sprites and alike.
olsu
olsu•2mo ago
im using primitives intentionally since my use is kinda specific and i want to use shaders anyway, i cant do stuff like this
vertices = new List<TrailVertex>();
vertices.Add(new TrailVertex());
vertices[0].Position = Vector2.Zero;
vertices = new List<TrailVertex>();
vertices.Add(new TrailVertex());
vertices[0].Position = Vector2.Zero;
because it wont let me modify vertices[0] and i dont want to create a new one every time since i'll be creating probably thousands or tens of thousands of vertices that will all be modified very often
SpReeD
SpReeD•2mo ago
Is Position readonly?
olsu
olsu•2mo ago
it's written as public Vector2 Position
SpReeD
SpReeD•2mo ago
How is it defined in TrailVertex?
olsu
olsu•2mo ago
the error reads "cannot modify because vertices[0] is not a variable" TrailVertex is a struct
struct TrailVertex
{
public Vector2 Position;

public TrailVertex(Vector2 position)
{
this.Position = position;
}
};
struct TrailVertex
{
public Vector2 Position;

public TrailVertex(Vector2 position)
{
this.Position = position;
}
};
cap5lut
cap5lut•2mo ago
there is a CollectionsMarshal.AsSpan(List<T>) helper method which u could use:
foreach (ref TrailVertex vertex in CollectionsMarshal.AsSpan(vertices))
{
vertex.Position = Vertex2.Zero;
}
foreach (ref TrailVertex vertex in CollectionsMarshal.AsSpan(vertices))
{
vertex.Position = Vertex2.Zero;
}
because vertex is a ref u directly write to the element in the list, not a copy to it
CollectionsMarshal.AsSpan(List) Method (System.Runtime.InteropServi...
Gets a Span view over the data in a list. Items should not be added or removed from the List while the Span is in use.
olsu
olsu•2mo ago
:/ im on .net 4
cap5lut
cap5lut•2mo ago
oh
SpReeD
SpReeD•2mo ago
On the other hand, TrailVertex is a custom object? Why not use a class instead? Or just Vector2 - structs should be used on small objects only and which represents a single point of data.
olsu
olsu•2mo ago
i simplified the code to not include other data im using per vertex i can try a class but like i said, im not sure how the layout will affect sending it to the gpu it seems sketchy since VertexPositionColor (an xna written vertex structure) is written as a struct like
[Serializable]
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct VertexPositionColor : IVertexType
{
public Vector3 Position;
...
[Serializable]
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct VertexPositionColor : IVertexType
{
public Vector3 Position;
...
SpReeD
SpReeD•2mo ago
VertexPositionColor implies a single of data, so that's okay. Dunno what TrailVertex will do, but if you leave it at Vector2, struct is fine. So vertices[0] complains about not a variable because it's a copy of a value type. Doing something like
TrailVertex a = vertices[0];
a.Position = ...
vertices[0] = a;
TrailVertex a = vertices[0];
a.Position = ...
vertices[0] = a;
olsu
olsu•2mo ago
honestly i think im just going to use raw arrays since that's copying data anyway i assume that's how List<T> implements resizing too, so copying once is better than copying twice since i think vertices[0].Position works on raw arrays
SpReeD
SpReeD•2mo ago
It does work, because there's no layer of a ref-type Plain array is just not so dynamic as a List or Collection, tho both uses arrays under the hood, but in a more complex way of calculating the initial size of the array etc.
olsu
olsu•2mo ago
yeah i imagined so i found a function Array.Resize<>() so i think i can make do
SpReeD
SpReeD•2mo ago
Yay, but that's considered rather low-level in c# and shouldn't be abused, people will tell you to use a List instead, unless you really know what you are doing. Also, as far as I remember, those are like strings, immutable types, so Resize does nothing but copy it to another array.
olsu
olsu•2mo ago
i dont think it's too low level to me, it seems like i need to know more about the language if i use List given the problem we've been talking about this whole time
SpReeD
SpReeD•2mo ago
Compared to other C languages it's way, way, way, more abstract, you cannot really do what real low-level is, like hardware near low-level, that's why cpp is still and will be a thing. The lowest you can go is by using the unsafe keyword, which allows you to use unmanaged code.
olsu
olsu•2mo ago
how advised against is unsafe since in cpp, im pretty much always writing unsafe code anyway i actually thought about fixing this issue by doing something similar to the cs equivalent of
std::vector<TrailVertex*>
vector.push_back(new TrailVertex(...))

//clean up memory
std::vector<TrailVertex*>
vector.push_back(new TrailVertex(...))

//clean up memory
since that way i'd be just getting the pointer to the actual instance i want to modify, but i figured c# isn't meant to be written that way
SpReeD
SpReeD•2mo ago
Mostly, unless you know what you are doing and you know how to manage the memory, GC won't hit there and you actually need to use destructors, which in a GC language ain't a thing. So the actual question is, why not use cpp in the first place.
olsu
olsu•2mo ago
unfortunately, i really cant-- im making a mod for a game that uses fna. I could use dllimports to port c++ into c#, but it's a pain since i need data from the c# side (stuff from the game's memory) that would need to go back and forth between c++ and c# me and some other guy already made a project that does this, but it's really innefficient since we're basically sending json files between the language and parsing that every frame
SpReeD
SpReeD•2mo ago
mh, sounds like you need some p/invokes and marshalling
olsu
olsu•2mo ago
maybe, it's hard since i dont know c# well enough to know what works and what doesnt and honestly by the time i do, i think writing c++ code might not even be necessary anymore also the biggest problem is that fna is written to support opengl, vulkan, and direct3d, but only uses hlsl for shaders, meaning in c++, i'd have to implement all 3 and build my own shader transpiler
SpReeD
SpReeD•2mo ago
you could write cpp dlls and via marshalling get the data from unmanaged back to actually managed code to make use of c#. okay, I see the problem here, kinda challanging, also by being bound to the old .net framework.
olsu
olsu•2mo ago
are you experienced doing things like that? the current project we wrote used c++ with imgui and imgui works by taking in a pointer to what data you want in the ui, then updates the value at the pointer depending on user input-- meaning you dont have to copy a bunch of stuff and i want to write an imgui wrapper so we can move all the logic to c# while not having to copy data
SpReeD
SpReeD•2mo ago
No, I did stuff, but definitely not experienced enough, but people at #allow-unsafe-blocks will help you, they are the cracks here.
olsu
olsu•2mo ago
alright, ill explore a bit and see what i can do