I am trying to build a simulation in C#. The model itself I want to adress as a struct. Inside the model are a lot of elements (I refer to them as cells, considering that elements also refer to the chemical elements in my program and that is confusing) Each cel is a struct. One of the data types inside is an array of floats.
Below is the code I wrote:
struct Model
{
//model consists of an array of cells
public Cel[] cel = new Cel[100];
public Model() { }
}
struct Cel
{
public float[] n = new float[3];
public Cel() { }
}
class Programs
{
static int Main()
{
Model model = new Model();
model.cel[1].n[1] = 0.2f;
Console.WriteLine(model.cel[1].n[1]);
//prevent console from closing
Console.ReadLine();
return 0;
}
}
When I run this, it compiles, but gives a message:
System.NullReferenceException: 'Object reference not set to an instance of an object.'
model.cel[].n was null. at line 24
model.cel[1].n[1] = 0.2f;
I think this is a logical way to do this and would like a solution to make it work. If this is not the way to do it, could someone point me in the right direction?
Thank you for your help.
CodePudding user response:
Array creation does not initialize each element inside the array - they are all defaults; you could do something like:
public Cel[] cel;
public Model() {
cel = new Cel[100];
for (int i = 0; i < cel.Length; i )
{
cel[i] = new Cel();
}
}
to initialize each manually, but... I'm not sure this is a great outcome, honestly. In particular, instead of a float[3]
, having 3 discreet float
fields in the Cel
struct would be a more typical implementation, perhaps making Model
a class
. You should also usually be very cautious of public fields and mutable structs.
Perhaps something like:
sealed class Model
{
//model consists of an array of cells
private readonly Cel[] cel = new Cel[100];
public ref Cel this[int index] => ref cel[index];
}
readonly struct Cel
{
public float X { get; }
public float Y { get; }
public float Z { get; }
public Cel(float x, float y, float z)
{
X = x;
Y = y;
Z = z;
}
public Cel WithX(float x) => new Cel(x, Y, Z);
public Cel WithY(float y) => new Cel(X, y, Z);
public Cel WithZ(float z) => new Cel(X, Y, z);
}
class Programs
{
static int Main()
{
Model model = new Model();
ref var cel = ref model[1];
cel = cel.WithY(0.2f);
Console.WriteLine(model[1].Y);
//prevent console from closing
Console.ReadLine();
return 0;
}
}
or with a fixed buffer and span hack:
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
sealed class Model
{
//model consists of an array of cells
private readonly Cel[] cel = new Cel[100];
public ref Cel this[int index] => ref cel[index];
}
unsafe struct Cel
{
private fixed float n[3];
public float X => n[0];
public float Y => n[1];
public float Z => n[2];
public Span<float> N
{
get
{
fixed (float* ptr = n)
{
return MemoryMarshal.CreateSpan(ref Unsafe.AsRef<float>(ptr), 3);
}
}
}
public Cel(float x, float y, float z)
{
fixed (float* ptr = n)
{
ptr[0] = x;
ptr[1] = y;
ptr[2] = z;
}
}
private Cel(in Cel from, int index, float value)
{
this = from;
n[index] = value;
}
public Cel WithX(float x) => new Cel(this, 0, x);
public Cel WithY(float y) => new Cel(this, 1, y);
public Cel WithZ(float z) => new Cel(this, 2, z);
}
class Programs
{
static int Main()
{
Model model = new Model();
ref var cel = ref model[1];
cel = cel.WithY(0.2f);
Console.WriteLine(model[1].Y);
Console.WriteLine();
foreach (var val in model[1].N) // and iterate via span
{
Console.WriteLine(val);
}
//prevent console from closing
Console.ReadLine();
return 0;
}
}
CodePudding user response:
You haven't initialized a Cel yet. You have only reserved space in the array, but it is still empty.
Try this:
model.cel[1] = new Cel();
model.cel[1].n[1] = 02f;
CodePudding user response:
The code
struct Cel
{
public float[] n = new float[3];
public Cel() { }
}
is not valid c# syntax. Structs cannot have parameterless constructors Cel()
and cannot have field initializers.
To make a struct with an array inside you have some options
Wrap the array if you want the size to be variable. The contents for the struct is just a reference to the array in memory and not the data itself.
struct Cel { public readonly float[] n; public Cel(int size) { n = new float[size]; } }
This is pretty useless as it is as it offers no extra functionality than the array itself. You might as well create a jagged array
float[][] model = new float[100][]; for(int i=0; i<100; i ) { model[i] = new float[3]; } // assign last value model[99][2] = 0.123f;
Declare the fields one by one to have an array of structures
struct Cel { public readonly float a, b, c; public Cel(float a, float b, float c) { this.a = a; this.b = b; this.c = c; } }
and then create an array of structures
Cel[] model = new Cel[100]; model[99] = new Cel(0.1f, 0.2f, 0.3f);
Note that the fields
a,b,c
go together and cannot (or should not be) individually assigned. This is assured with thereadonly
keyword making the fields read-only, assignable only by the constructorCel(float a, float b, float c)
There are more advanced options with fixed-sized buffers (see the fixed
keyword) and unsafe code.