The first thing you may have noticed about the fill extension method was the improper generic constraints. This was due to Array being extended rather than specific arrays. To correct this, it is necessary to set the original method to private and implement overloads for each multidimensional array. Here are the overloads for one and two dimensions.
public static void Fill<T>(this T[] array, Func<int[], T> func)
{
Fill((Array)array, func);
}
public static void Fill<T>(this T[,] array, Func<int[], T> func)
{
Fill((Array)array, func);
}
It was brought to my attention by Scott Briercheck at Precyse Solutions that this may cause performance issues in large multidimensional arrays. Indeed, my testing proved that it does. Consider the following example.
int[,] nums = new int[10000, 10000];
for (int x = 0; x < 10000; x++)
for (int y = 0; y < 10000; y++)
nums[x, y] = x + y;
On my computer, this takes nearly two seconds to execute. If I were to rewrite this as nums.Fill(i => i.Sum()), it would take nearly 27 seconds! That is a huge difference, but part of that is due to the LINQ to Objects call in the lambda statement. If that were changed to i[0] + i[1], it reduces the time to 17 seconds. Still, the performance is horrible for large arrays.
The differences between the extension method and the imperative way of writing the code are primarily related to the use of delegates, the known number of ranks, and the known number of elements. We just finished refactoring the code to correct a mistake in the generic constraints, and it gives us information about the known number of ranks. We can use this to optimize the extension methods. We can’t completely eliminate the functions, but since a single extension method for each multidimensional array ranking is included we can change the signature of the functions which may help.
public static void Fill<T>(this T[] array, Func<int, T> func)
{
for (int a = array.GetLowerBound(0); a < array.GetUpperBound(0); a++)
{
array[a] = func(a);
}
}
public static void Fill<T>(this T[,] array, Func<int, int, T> func)
{
for (int a = array.GetLowerBound(0); a < array.GetUpperBound(0); a++)
{
for (int b = array.GetLowerBound(1); b < array.GetUpperBound(1); b++)
{
array[a, a] = func(a, b);
}
}
}
I can then test our original scenario using the new extension method.
int[,] nums = new int[10000, 10000];
nums.Fill((x, y) => x + y);
My timing on it comes in just over six seconds. That is a vast improvement over the original!