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:

\$\endgroup\$
2
  • 1
    \$\begingroup\$ @aepot Thank you for your comments. Already updated. \$\endgroup\$
    – JimmyHu
    Jan 31 at 15:31
  • 1
    \$\begingroup\$ As for me, looks fine now. \$\endgroup\$
    – aepot
    Jan 31 at 15:33
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);
        }
    }
}
```
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.