如何在包含数组的类之外引用数组元素?

时间:2012-10-10 11:53:06

标签: c# reference

我在课堂上有一系列课程(Voxel)。我使用以下方法添加和删除数组。 memento模式用于存储每个方法的动作,因此可以在任何时候撤消/重做。

    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
    }

在另一个类中,我希望引用上述类所持有的体素数组中的特定体素。

private Voxel voxelSelected = null;

作为参考类型,我希望这个值能够自动知道它“指向”数组的部分何时保持体素或为空。这在使用撤消命令时很重要,因为体素可以从数组中删除并变为空,反之亦然。

要从数组中获取体素,我使用以下方法。

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

然后我按如下方式设置对体素的引用。

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)

如何正确引用数组中的体素,以便在阵列更新时voxelSelected值自动更新。

3 个答案:

答案 0 :(得分:2)

  

如何正确引用数组中的体素,以便在阵列更新时voxelSelected值自动更新。

不要存储Voxel,而是存储获取体素所需的坐标。

而不是

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

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

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

答案 1 :(得分:1)

您可以尝试使用以下包装器:

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;
    }
}

并且传递VoxelReference的实例而不是Voxel。然后,您可以通过以下方式使用它:((Voxel)voxelReference)voxelReference.Target

当然,您可能希望将上述类更改为T的通用类。 或者存储坐标而不是索引。

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;
    }
}

答案 2 :(得分:0)

如果<{strong> Voxelreference类型,您已经有了直接引用,以您在代码中呈现的方式执行此操作提供。

请注意,使用Undo/Redo轻松获得value types,其中Lightweight结构仅包含适用于Undo/Redo的属性和分配给它们的值。

在这种情况下,具有不变性,使事情变得更容易。

但是,当然,我不知道在你的具体情况下这是否是一个方便的选择。

在你的例子中:

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 ! */