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.
>Solution :
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;
}
}