Problem
This is a follow-up question for ConvertAll Methods Implementation for Multidimensional Array in C#. Thanks to aepot’s answer and Olivier’s answer. In order to match the usage of the build-in API Array.ConvertAll
, the input /output array types of the implemented methods below are similar to []
, [,]
, [,,]
and etc. style.
The experimental implementation
The experimental implementation is as below.
public static TOutput[,] ConvertAll<TInput, TOutput>(TInput[,] array, Converter<TInput, TOutput> converter)
where TInput : unmanaged
where TOutput : unmanaged
{
return (TOutput[,])ConvertArray(array, converter);
}
public static TOutput[,,] ConvertAll<TInput, TOutput>(TInput[,,] array, Converter<TInput, TOutput> converter)
where TInput : unmanaged
where TOutput : unmanaged
{
return (TOutput[,,])ConvertArray(array, converter);
}
public static TOutput[,,,] ConvertAll<TInput, TOutput>(TInput[,,,] array, Converter<TInput, TOutput> converter)
where TInput : unmanaged
where TOutput : unmanaged
{
return (TOutput[,,,])ConvertArray(array, converter);
}
public static TOutput[,,,,] ConvertAll<TInput, TOutput>(TInput[,,,,] array, Converter<TInput, TOutput> converter)
where TInput : unmanaged
where TOutput : unmanaged
{
return (TOutput[,,,,])ConvertArray(array, converter);
}
public static TOutput[,,,,,] ConvertAll<TInput, TOutput>(TInput[,,,,,] array, Converter<TInput, TOutput> converter)
where TInput : unmanaged
where TOutput : unmanaged
{
return (TOutput[,,,,,])ConvertArray(array, converter);
}
public static TOutput[,,,,,,] ConvertAll<TInput, TOutput>(TInput[,,,,,,] array, Converter<TInput, TOutput> converter)
where TInput : unmanaged
where TOutput : unmanaged
{
return (TOutput[,,,,,,])ConvertArray(array, converter);
}
public static TOutput[,,,,,,,] ConvertAll<TInput, TOutput>(TInput[,,,,,,,] array, Converter<TInput, TOutput> converter)
where TInput : unmanaged
where TOutput : unmanaged
{
return (TOutput[,,,,,,,])ConvertArray(array, converter);
}
public static TOutput[,,,,,,,,] ConvertAll<TInput, TOutput>(TInput[,,,,,,,,] array, Converter<TInput, TOutput> converter)
where TInput : unmanaged
where TOutput : unmanaged
{
return (TOutput[,,,,,,,,])ConvertArray(array, converter);
}
public static TOutput[,,,,,,,,,] ConvertAll<TInput, TOutput>(TInput[,,,,,,,,,] array, Converter<TInput, TOutput> converter)
where TInput : unmanaged
where TOutput : unmanaged
{
return (TOutput[,,,,,,,,,])ConvertArray(array, converter);
}
static Array ConvertArray<T, TResult>(Array array, Converter<T, TResult> converter)
where T : unmanaged
where TResult : unmanaged
{
if (array is null)
{
throw new ArgumentNullException(nameof(array));
}
if (converter is null)
{
throw new ArgumentNullException(nameof(converter));
}
long[] dimensions = new long[array.Rank];
for (int i = 0; i < array.Rank; i++)
dimensions[i] = array.GetLongLength(i);
Array result = Array.CreateInstance(typeof(TResult), dimensions);
TResult[] tmp = new TResult[1];
int offset = 0;
int itemSize = System.Runtime.InteropServices.Marshal.SizeOf(typeof(TResult));
foreach (T item in array)
{
tmp[0] = converter(item);
Buffer.BlockCopy(tmp, 0, result, offset * itemSize, itemSize);
offset++;
}
return result;
}
All suggestions are welcome.
The summary information:
-
Which question it is a follow-up to?
ConvertAll Methods Implementation for Multidimensional Array in C#
-
What changes has been made in the code since last question?
Trying to perform higher dimensional overloadings and exception handling based on aepot’s answer.
-
Why a new review is being asked for?
If there is any possible improvement or any other potential defect, please let me know.
Solution
When I see that many recurrences like this then it is a good sign for me that this is a perfect place to introduce meta-programming via T4.
Step #1
Separate the overloads from the core logic:
using System;
namespace Sample.Core
{
internal static class Converter
{
public static Array ConvertArray<T, TResult>(Array array, Converter<T, TResult> converter)
where T : unmanaged
where TResult : unmanaged
{
if (array is null)
{
throw new ArgumentNullException(nameof(array));
}
if (converter is null)
{
throw new ArgumentNullException(nameof(converter));
}
long[] dimensions = new long[array.Rank];
for (int i = 0; i < array.Rank; i++)
dimensions[i] = array.GetLongLength(i);
Array result = Array.CreateInstance(typeof(TResult), dimensions);
TResult[] tmp = new TResult[1];
int offset = 0;
int itemSize = System.Runtime.InteropServices.Marshal.SizeOf(typeof(TResult));
foreach (T item in array)
{
tmp[0] = converter(item);
Buffer.BlockCopy(tmp, 0, result, offset * itemSize, itemSize);
offset++;
}
return result;
}
}
}
Step #2
Introduce a *.tt
file with the following content:
<#@ template language="C#" #>
<#@ assembly name="System.Core" #>
<#@ import namespace="System.Linq" #>
<#@ import namespace="System.Text" #>
<#@ import namespace="System.Collections.Generic" #>
using System;
using static Sample.Core.Converter;
namespace Sample.Overloads
{
public static class ArrayConverter
{
<#
for(int i = 1; i < 10; i++)
{
var dimensions = new String(',', i);
#>
public static TOutput[<#=dimensions#>] ConvertAll<TInput, TOutput>(TInput[<#=dimensions#>] array, Converter<TInput, TOutput> converter)
where TInput : unmanaged
where TOutput : unmanaged
{
return (TOutput[<#=dimensions#>])ConvertArray(array, converter);
}
<#
}
#>
}
}
Step #3
Execute the tt
file and examine the generated code:
using System;
using static Sample.Core.Converter;
namespace Sample.Overloads
{
public static class ArrayConverter
{
public static TOutput[,] ConvertAll<TInput, TOutput>(TInput[,] array, Converter<TInput, TOutput> converter)
where TInput : unmanaged
where TOutput : unmanaged
{
return (TOutput[,])ConvertArray(array, converter);
}
public static TOutput[,,] ConvertAll<TInput, TOutput>(TInput[,,] array, Converter<TInput, TOutput> converter)
where TInput : unmanaged
where TOutput : unmanaged
{
return (TOutput[,,])ConvertArray(array, converter);
}
public static TOutput[,,,] ConvertAll<TInput, TOutput>(TInput[,,,] array, Converter<TInput, TOutput> converter)
where TInput : unmanaged
where TOutput : unmanaged
{
return (TOutput[,,,])ConvertArray(array, converter);
}
public static TOutput[,,,,] ConvertAll<TInput, TOutput>(TInput[,,,,] array, Converter<TInput, TOutput> converter)
where TInput : unmanaged
where TOutput : unmanaged
{
return (TOutput[,,,,])ConvertArray(array, converter);
}
public static TOutput[,,,,,] ConvertAll<TInput, TOutput>(TInput[,,,,,] array, Converter<TInput, TOutput> converter)
where TInput : unmanaged
where TOutput : unmanaged
{
return (TOutput[,,,,,])ConvertArray(array, converter);
}
public static TOutput[,,,,,,] ConvertAll<TInput, TOutput>(TInput[,,,,,,] array, Converter<TInput, TOutput> converter)
where TInput : unmanaged
where TOutput : unmanaged
{
return (TOutput[,,,,,,])ConvertArray(array, converter);
}
public static TOutput[,,,,,,,] ConvertAll<TInput, TOutput>(TInput[,,,,,,,] array, Converter<TInput, TOutput> converter)
where TInput : unmanaged
where TOutput : unmanaged
{
return (TOutput[,,,,,,,])ConvertArray(array, converter);
}
public static TOutput[,,,,,,,,] ConvertAll<TInput, TOutput>(TInput[,,,,,,,,] array, Converter<TInput, TOutput> converter)
where TInput : unmanaged
where TOutput : unmanaged
{
return (TOutput[,,,,,,,,])ConvertArray(array, converter);
}
public static TOutput[,,,,,,,,,] ConvertAll<TInput, TOutput>(TInput[,,,,,,,,,] array, Converter<TInput, TOutput> converter)
where TInput : unmanaged
where TOutput : unmanaged
{
return (TOutput[,,,,,,,,,])ConvertArray(array, converter);
}
}
}
```