Array.ConvertAll()
only handles one-dimensional arrays. The input is expected to be an array of the input type and produces an array of the output type. The converter is expecting something that can convert object[,]
-> string[,]
.
Multi-dimensional arrays do not have generic convert method, you would have to do it by hand. You could do something like this:
public TOutput[,] ConvertAll<TInput, TOutput>(TInput[,] input, Func<TInput, TOutput> converter)
{
var length0 = input.GetLength(0);
var length1 = input.GetLength(1);
var result = new TOutput[length0, length1];
for (var i = 0; i < length0; ++i)
for (var j = 0; j < length1; ++j)
result[i, j] = converter(input[i, j]);
return result;
}
Then you could do this to do the conversion:
var input = GetSelectedRange();
var converted = ConvertAll(input, x => x.ToString());
If it's possible that the source array is not 0-based, some adjustments would need to be made to handle the changed bounds.
public TOutput[,] ConvertAll<TInput, TOutput>(TInput[,] input, Func<TInput, TOutput> converter)
{
var lengths = new[] { input.GetLength(0), input.GetLength(1) };
var lowers = new[] { input.GetLowerBound(0), input.GetLowerBound(1) };
var uppers = new[] { input.GetUpperBound(0), input.GetUpperBound(1) };
var result = Array.CreateInstance(typeof(TOutput), lengths, lowers) as TOutput[,];
for (int i = lowers[0], il = uppers[0]; i <= il; ++i)
for (int j = lowers[1], jl = uppers[1]; j <= jl; ++j)
result[i, j] = converter(input[i, j]);
return result;
}
For a general purpose converter that can handle all arrays, this could be used.
public Array ConvertAll<TInput, TOutput>(Array input, Func<TInput, TOutput> converter)
{
var rank = input.Rank;
var lengths = Enumerable.Range(0, rank).Select(i => input.GetLength(i)).ToArray();
var lowerBounds = Enumerable.Range(0, rank).Select(i => input.GetLowerBound(i)).ToArray();
var upperBounds = Enumerable.Range(0, rank).Select(i => input.GetUpperBound(i));
var output = Array.CreateInstance(typeof(TOutput), lengths, lowerBounds);
ConvertRank(input, converter, output, new int[0], lowerBounds.Zip(upperBounds, Tuple.Create).ToArray());
return output;
}
private void ConvertRank<TInput, TOutput>(Array input, Func<TInput, TOutput> converter, Array output, int[] indices, Tuple<int, int>[] bounds)
{
if (!bounds.Any())
{
var value = input.GetValue(indices);
var convertedValue = converter((TInput)value);
output.SetValue(convertedValue, indices);
}
else
{
var start = bounds[0].Item1;
var end = bounds[0].Item2;
var tail = bounds.Skip(1).ToArray();
for (var i = start; i <= end; i++)
ConvertRank(input, converter, output, indices.Concat(new[] { i }).ToArray(), tail);
}
}
You'll just need to cast back the array to the expected type (since the rank could not be expressed).
object[,,] input = GetSome3dObjectArray();
var converted = ConvertAll(input, (object x) => x.ToString()) as string[,,];
If you wanted some strong typing, you could create the appropriate overloads for the types you want to support.
public TOutput[,] ConvertAll<TInput, TOutput>(TInput[,] input, Func<TInput, TOutput> converter) =>
ConvertAll((Array)input, converter) as TOutput[,];
public TOutput[,,] ConvertAll<TInput, TOutput>(TInput[,,] input, Func<TInput, TOutput> converter) =>
ConvertAll((Array)input, converter) as TOutput[,,];