Home > database >  c# arrays in nested structs
c# arrays in nested structs

Time:08-08

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

  1. 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;    
    
  2. 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 the readonly keyword making the fields read-only, assignable only by the constructor Cel(float a, float b, float c)

There are more advanced options with fixed-sized buffers (see the fixed keyword) and unsafe code.

  • Related