1
\$\begingroup\$

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:

asked Jan 29, 2021 at 22:36
\$\endgroup\$
2
  • 1
    \$\begingroup\$ @aepot Thank you for your comments. Already updated. \$\endgroup\$ Commented Jan 31, 2021 at 15:31
  • 1
    \$\begingroup\$ As for me, looks fine now. \$\endgroup\$ Commented Jan 31, 2021 at 15:33

1 Answer 1

3
\$\begingroup\$

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);
 }
 }
}
```
answered Feb 1, 2021 at 14:13
\$\endgroup\$

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.