tags:

views:

244

answers:

2

I want write extension method for fill multidimensional rectangular array. I know how to do it for the array with a fixed number of measurements:

public static void Fill<T>(this T[] source, T value)
{
    for (int i = 0; i < source.Length; i++)
        source[i] = value;
}
public static void Fill<T>(this T[,] source, T value)
{
    for (int i = 0; i < source.GetLength(0); i++)
        for (int j = 0; j < source.GetLength(1); j++)
            source[i, j] = value;
}
public static void Fill<T>(this T[,,] source, T value)
{
    for (int i = 0; i < source.GetLength(0); i++)
        for (int j = 0; j < source.GetLength(1); j++)
            for (int k = 0; k < source.GetLength(2); k++)
                source[i, j, k] = value;
}

Can I write one fill-method for all multidimensional rectangular array?

+4  A: 

You can change the fixed dimension parameter to an Array parameter so you can put the extension on any Array. Then I used recursion to iterate through each position of the array.

public static void Fill<T>(this Array source, T value)
{
 Fill(0, source, new long[source.Rank], value);
}

static void Fill<T>(int dimension, Array array, long[] indexes, T value)
{
 var lowerBound = array.GetLowerBound(dimension);
 var upperBound = array.GetUpperBound(dimension);
 for (int i = lowerBound; i <= upperBound; i++)
 {
  indexes[dimension] = i;
  if (dimension < array.Rank - 1)
  {
   Fill(dimension + 1, array, indexes, value);
  }
  else
  {
   array.SetValue(value, indexes);
  }
 }
}
Jake Pearson
Nice solution. How would you modify your solution to make it work on a multidimensional array of *pointers to integer*, in unsafe code?
Eric Lippert
I tried `array.SetValue(` but it won't compile. Is there a way to cast/convert an object to an int* in C#?
Jake Pearson
+1  A: 

Here's a solution that does not use recursion (and is less complex):

   public static void FillFlex<T>(this Array source, T value)
    {

        bool complete = false;
        int[] indices = new int[source.Rank];
        int index = source.GetLowerBound(0);
        int totalElements = 1;

        for (int i = 0; i < source.Rank; i++)
        {
            indices[i] = source.GetLowerBound(i);
            totalElements *= source.GetLength(i);
        }
        indices[indices.Length - 1]--;
        complete = totalElements == 0;

        while (!complete)
        {
            index++;

            int rank = source.Rank;
            indices[rank - 1]++;
            for (int i = rank - 1; i >= 0; i--)
            {
                if (indices[i] > source.GetUpperBound(i))
                {
                    if (i == 0)
                    {
                        complete = true;
                        return;
                    }
                    for (int j = i; j < rank; j++)
                    {
                        indices[j] = source.GetLowerBound(j);
                    }
                    indices[i - 1]++;
                }
            }

            source.SetValue(value, indices);
        }
    }

This is modeled from the System.Array.ArrayEnumerator. This implementation should have a similar level of correctness as ArrayEnumerator and (based on a few spot checks) appears to function fine.

Robert Venables