Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

c# arrays in nested structs

Tags:

c#

struct

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.

like image 808
Kvaestr Avatar asked Jul 20 '26 20:07

Kvaestr


2 Answers

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;
    }
}
like image 136
Marc Gravell Avatar answered Jul 22 '26 09:07

Marc Gravell


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;
like image 22
Joost00719 Avatar answered Jul 22 '26 09:07

Joost00719