C# 用一维索引访问多维数组

C# 用一维索引访问多维数组,c#,.net,C#,.net,请参阅下面的代码片段。使用一维索引访问多维数组的方法是什么。Foreach可以做到这一点。是的,我知道,收益率和指数是不一样的。我应该使用foreach并创建一个新数组吗?或者我可以在不创建新阵列的情况下执行此操作吗 int[,] myArray = new int[2, 2]; myArray[0,0] = 1; myArray[1,1] = 2; myArray[0,0] = 3; myArray[1,1] = 4; foreach (var

请参阅下面的代码片段。使用一维索引访问多维数组的方法是什么。Foreach可以做到这一点。是的,我知道,收益率和指数是不一样的。我应该使用foreach并创建一个新数组吗?或者我可以在不创建新阵列的情况下执行此操作吗

    int[,] myArray = new int[2, 2];
    myArray[0,0] = 1;
    myArray[1,1] = 2;
    myArray[0,0] = 3;
    myArray[1,1] = 4;
    foreach (var value in myArray)
    {
        Console.Write(value);
    }

    var valueAtIndex = myArray[2]; //Won't compile, error: Wrong number of indices inside []; expected 2

如果一维索引是指按从上到下、从左到右的顺序围绕多维数组行的索引,则可以使用以下公式计算
x
y

x = index % width
y = index / width

如果一维索引是指按从上到下、从左到右的顺序围绕多维数组行的索引,则可以使用以下公式计算
x
y

x = index % width
y = index / width

从另一端把它挖出来怎么样

int rows=2, cols=2;
int[] myArray = new int[rows*cols];

public void setMyArray(int[] myArray, int x, int y, int value)
{
    myArray[(y)*cols + x] = value;
}

setMyArray(0,0,1);
setMyArray(0,1,2);
setMyArray(1,0,3);
setMyArray(1,1,4);

从另一端把它挖出来怎么样

int rows=2, cols=2;
int[] myArray = new int[rows*cols];

public void setMyArray(int[] myArray, int x, int y, int value)
{
    myArray[(y)*cols + x] = value;
}

setMyArray(0,0,1);
setMyArray(0,1,2);
setMyArray(1,0,3);
setMyArray(1,1,4);

至于枚举器,它非常简单——它只允许顺序访问。在内部,它保存一个索引数组并逐个递增。它没有使用任何黑客手段使数组成为一维或任何东西——这将是对框架保证的一种丑陋的违反。而且也没有收益,这是在收益实现之前很久写的:)

当然,你可以自己编写这样的黑客程序,使用
safe
code。然而,这是一个非常糟糕的主意。您并不知道这些数组在内存中是如何组织的——这是运行时的一个实现细节。别这样

相反,找出一个合理的寻址模式。如果只有一个索引是有意义的,那么为什么数组首先是多维的呢?如果多维数组是有意义的,为什么要以一维方式访问它

如果它确实有意义,出于某种原因,您可能希望围绕某个内部数组创建自己的包装器类,而不仅仅是使用数组。这很简单

public class DualAccessArray<T>
{
  private readonly T[,] _internalArray;
  private readonly int _width;
  private readonly int _height;

  public DualAccessArray(int width, int height)
  {
    _width = width;
    _height = height;
    _internalArray = new T[width, height];
  }

  public T this[long index]
  {
    get { return _internalArray[index / _width, index % _width]; }
    set { _internalArray[index / _width, index % _width] = value; }
  }

  public T this[int x, int y]
  {
    get { return _internalArray[x, y]; }
    set { _internalArray[x, y] = value; }
  }
}
公共类DualAccessArray
{
私有只读T[,]\u内部数组;
私有只读整数宽度;
专用只读int_高度;
公共DualAccessArray(整数宽度、整数高度)
{
_宽度=宽度;
_高度=高度;
_internalArray=新的T[宽度、高度];
}
这是什么[长索引]
{
获取{return _internalArray[index/_width,index%_width];}
设置{u internalArray[index/\u width,index%\u width]=value;}
}
公共T这个[int x,int y]
{
获取{return _internalArray[x,y];}
设置{u internalArray[x,y]=value;}
}
}
然后你可以像这样使用它

var ar = new DualAccessArray<string>(10, 10);
ar[15] = "Hi!";
Console.WriteLine(ar[1, 5]);
var-ar=新的DualAccessArray(10,10);
ar[15]=“嗨!”;
控制台写入线(ar[1,5]);

你可以根据自己的需要来做。例如,使用不同类型的寻址和内存布局可能更有意义,使用一维数组可能更有意义,您可能希望包装现有数组。。。这完全取决于您实际要做什么。

至于枚举器,它非常简单-它只允许顺序访问。在内部,它保存一个索引数组并逐个递增。它没有使用任何黑客手段使数组成为一维或任何东西——这将是对框架保证的一种丑陋的违反。而且也没有收益,这是在收益实现之前很久写的:)

当然,你可以自己编写这样的黑客程序,使用
safe
code。然而,这是一个非常糟糕的主意。您并不知道这些数组在内存中是如何组织的——这是运行时的一个实现细节。别这样

相反,找出一个合理的寻址模式。如果只有一个索引是有意义的,那么为什么数组首先是多维的呢?如果多维数组是有意义的,为什么要以一维方式访问它

如果它确实有意义,出于某种原因,您可能希望围绕某个内部数组创建自己的包装器类,而不仅仅是使用数组。这很简单

public class DualAccessArray<T>
{
  private readonly T[,] _internalArray;
  private readonly int _width;
  private readonly int _height;

  public DualAccessArray(int width, int height)
  {
    _width = width;
    _height = height;
    _internalArray = new T[width, height];
  }

  public T this[long index]
  {
    get { return _internalArray[index / _width, index % _width]; }
    set { _internalArray[index / _width, index % _width] = value; }
  }

  public T this[int x, int y]
  {
    get { return _internalArray[x, y]; }
    set { _internalArray[x, y] = value; }
  }
}
公共类DualAccessArray
{
私有只读T[,]\u内部数组;
私有只读整数宽度;
专用只读int_高度;
公共DualAccessArray(整数宽度、整数高度)
{
_宽度=宽度;
_高度=高度;
_internalArray=新的T[宽度、高度];
}
这是什么[长索引]
{
获取{return _internalArray[index/_width,index%_width];}
设置{u internalArray[index/\u width,index%\u width]=value;}
}
公共T这个[int x,int y]
{
获取{return _internalArray[x,y];}
设置{u internalArray[x,y]=value;}
}
}
然后你可以像这样使用它

var ar = new DualAccessArray<string>(10, 10);
ar[15] = "Hi!";
Console.WriteLine(ar[1, 5]);
var-ar=新的DualAccessArray(10,10);
ar[15]=“嗨!”;
控制台写入线(ar[1,5]);

你可以根据自己的需要来做。例如,使用不同类型的寻址和内存布局可能更有意义,使用一维数组可能更有意义,您可能希望包装现有数组。。。这完全取决于您实际尝试执行的操作。

如果您不仅希望读取值,还希望按照
foreach
遍历数组的相同顺序设置值,则可以使用以下通用索引器类:

public class ArrayIndexer
{
    readonly int totalLength;
    readonly int lastIndexLength;
    readonly int[] lengths;
    readonly int[] lowerBounds;
    int current;
    readonly int[] currentZeroBased;

    public ArrayIndexer(int[] lengths, int[] lowerBounds)
    {
        lastIndexLength = lengths[lengths.Length - 1];
        totalLength = lengths[0];
        for (int i = 1; i < lengths.Length; i++)
        {
            totalLength *= lengths[i];
        }
        this.lengths = lengths;
        this.lowerBounds = lowerBounds;
        currentZeroBased = new int[lengths.Length];
        current = -1;
    }

    public bool MoveNext()
    {
        current++;
        if (current != 0)
        {
            int currLastIndex = current % lastIndexLength;
            currentZeroBased[currentZeroBased.Length - 1] = currLastIndex;
            if (currLastIndex == 0)
            {
                for (int i = currentZeroBased.Length - 2; i >= 0; i--)
                {
                    currentZeroBased[i]++;
                    if (currentZeroBased[i] != lengths[i])
                        break;
                    currentZeroBased[i] = 0;
                }
            }
        }
        return current < totalLength;
    }

    public int[] Current
    {
        get
        {
            int[] result = new int[currentZeroBased.Length];
            for (int i = 0; i < result.Length; i++)
            {
                result[i] = currentZeroBased[i] + lowerBounds[i];
            }
            return result;
        }
    }
}

如果您不仅希望读取值,还希望按照
foreach
遍历数组的相同顺序设置值,则可以使用以下常规索引器类:

public class ArrayIndexer
{
    readonly int totalLength;
    readonly int lastIndexLength;
    readonly int[] lengths;
    readonly int[] lowerBounds;
    int current;
    readonly int[] currentZeroBased;

    public ArrayIndexer(int[] lengths, int[] lowerBounds)
    {
        lastIndexLength = lengths[lengths.Length - 1];
        totalLength = lengths[0];
        for (int i = 1; i < lengths.Length; i++)
        {
            totalLength *= lengths[i];
        }
        this.lengths = lengths;
        this.lowerBounds = lowerBounds;
        currentZeroBased = new int[lengths.Length];
        current = -1;
    }

    public bool MoveNext()
    {
        current++;
        if (current != 0)
        {
            int currLastIndex = current % lastIndexLength;
            currentZeroBased[currentZeroBased.Length - 1] = currLastIndex;
            if (currLastIndex == 0)
            {
                for (int i = currentZeroBased.Length - 2; i >= 0; i--)
                {
                    currentZeroBased[i]++;
                    if (currentZeroBased[i] != lengths[i])
                        break;
                    currentZeroBased[i] = 0;
                }
            }
        }
        return current < totalLength;
    }

    public int[] Current
    {
        get
        {
            int[] result = new int[currentZeroBased.Length];
            for (int i = 0; i < result.Length; i++)
            {
                result[i] = currentZeroBased[i] + lowerBounds[i];
            }
            return result;
        }
    }
}
Foreach可以做到这一点

不,不能。真的没有类比

由于多维数组实际上是一个具有多个维度的数组,因此有三个选项

  • 停止使用多维数组。如果这种操作在您的使用中占主导地位,那么数组数组(“锯齿数组”)可能更符合您的需要

  • 忽略数组的细节并使用指向内存的指针。如果没有必要,最好避免

  • 只需使用两个索引。您可以很容易地找到其他索引的范围