user1423893
user1423893

Reputation: 796

How can I reference an array element outside the class that holds the array?

I have an array of classes (Voxel) in a class. I use the following methods to add to and remove from the array. A memento pattern is used to store the action for each method so it can be undone/redone at any point.

    public void AddVoxel(int x, int y, int z)
    {
        int index = z * width * height + y * width + x;

        frames[currentFrame].Voxels[index] = new Voxel();

        // Undo/Redo history
        undoRedoHistories[currentFrame].Do(new AddMemento(index));
    }

    public void RemoveVoxel(int x, int y, int z)
    {
        int index = z * width * height + y * width + x;

        // Undo/Redo history
        undoRedoHistories[currentFrame].Do(new RemoveMemento(index, frames[currentFrame].Voxels[index]));

        frames[currentFrame].Voxels[index] = null;      // Does not update 'voxelSelected' reference
    }

In a separate class I wish to have a reference to a particular voxel in the array of voxels held by the above class.

private Voxel voxelSelected = null;

As a reference type I would like this value to automatically know when the part of the array it "points" to holds a voxel or is null. This is important when using an undo command because a voxel can be removed from the array and become null, or vice versa.

To get a voxel from the array I use the following method.

public Voxel GetVoxel(int x, int y, int z)
{
    return frames[currentFrame].Voxels[z * width * height + y * width + x];
}

I then set the reference to the voxel as follows.

public void SetVoxelSelected(ref Voxel voxel)
{
    voxelSelected = voxel;
}

    voxelMeshEditor.AddVoxel(0, 0, 0);

    var voxel = voxelMeshEditor.GetVoxel(0, 0, 0);  // Copies rather than references?

    SetVoxelSelected(ref voxel);

    Console.WriteLine(voxelSelected == null); // False

    voxelMeshEditor.RemoveVoxel(0, 0, 0);

    Console.WriteLine(voxelSelected == null); // False (Incorrect intended behaviour)

How can I correctly reference a voxel in the array so the voxelSelected value automatically updates when the array updates.

Upvotes: 2

Views: 1577

Answers (3)

Nick Hill
Nick Hill

Reputation: 4927

You can try to use the following wrapper:

public class VoxelReference
{
    private readonly Voxel[] m_array;
    private readonly int m_index;

    public Voxel Target
    {
        get { return m_array[m_index]; }
    }

    public VoxelReference(Voxel[] array, int index)
    {
        m_array = array;
        m_index = index;
    }

    public static explicit operator Voxel(VoxelReference reference)
    {
        return reference.Target;
    }
}

And pass an instance of VoxelReference everywhere instead of Voxel. Then you can use it in the following ways: ((Voxel)voxelReference) or voxelReference.Target

Of course, you might want to change the above class to be generic of T. Or to store the coordinates instead of index.

public class ArrayElementReference<T>
{
    private readonly T[] m_array;
    private readonly int m_index;

    public T Target
    {
        get { return m_array[m_index]; }
    }

    public ArrayElementReference(T[] array, int index)
    {
        m_array = array;
        m_index = index;
    }

    public static explicit operator T(ArrayElementReference<T> reference)
    {
        return reference.Target;
    }
}

Upvotes: 1

Rawling
Rawling

Reputation: 50214

How can I correctly reference a voxel in the array so the voxelSelected value automatically updates when the array updates.

Don't store a Voxel, but store the coordinates you need to get the voxel.

Instead of

Voxel voxelSelected;
public void SetVoxelSelected(ref Voxel voxel) { voxelSelected = voxel; }

have

int selectedX, selectedY, selectedZ
public void SetVoxelSelected (int x, int y, int z) { selectedX = x; ... }
public Voxel GetVoxelSelected() { return voxelMeshEditor.GetVoxel(x, y, z) }

giving

SetVoxelSelected(0, 0, 0);                     //Set selected
Console.WriteLine(GetVoxelSelected() == null); // False, CORRECT !
voxelMeshEditor.RemoveVoxel(0, 0, 0);          //REMOVE
Console.WriteLine(GetVoxelSelected() == null); // True

Upvotes: 2

Tigran
Tigran

Reputation: 62265

If Voxel is a reference type, you already have a direct reference to it, doing it in a way you present in code provided.

Should note, that Undo/Redo easily achived with value types, with some Lightweight structure that holds only properties suitable for Undo/Redo and values assigned to them.

Having immutability, in this case, make things a lot easier.

But, naturally, I don't know if this would be a convinient choice in your specific case.

In your example:

var voxel = voxelMeshEditor.GetVoxel(0, 0, 0);  // Copy REFERENCE, and NOT value

SetVoxelSelected(ref voxel);   //Set selctede vortex reference

Console.WriteLine(voxelSelected == null); // False, CORRECT !

voxelMeshEditor.RemoveVoxel(0, 0, 0); //REMOVE

Console.WriteLine(voxelSelected == null); 
/* Selected vortex still pointing to the memroy location it was pointing before. 
   The fact is that memory location is not more in array, doesn't matter. 
   So CORRECT ! */

Upvotes: 0

Related Questions