How to get reference to first element of Array

Viewed 102

I'm having a converter routine that converts/casts from on array type into another. Originally the method looked like this:

static void Convert<TTo>(Array source, TTo[] target);

But there is a need to go with Span in the future, so the new method should look like this

static void Convert<TTo>(Array source, in Span<TTo> target)

Currently I call the conversions in my internal routines

public double GetSumOfAbsoluteDifferences(Array imageData1, Add imageData2)
{
  // Convert to float
  float[] id1 = new float[imageData1.Length];
  float[] id2 = new float[imageData2.Length];

  DataConversion.Convert<float>(imageData1, id1);
  DataConversion.Convert<float>(imageData2, id2);

  double sum=0;
  for (int i=0; i<imageData1.Length; i++)
  {
    sum += Math.Abs(id1[i] - id2[i]);
  }

  return sum;
}

The old method was able to convert any dimension of the array into a one dimensional array which is OK. Originally I had to do a lot of unsafe code to make the method as generic as possible, at least for struct or unmanaged types. Since I have no typed version of Array I needed to do a GCHandle.Alloc and pin the array for the time of the cast. My current implementation looks like this (for brevity only one converter is shown):

public unsafe static class DataConversion
{
    public delegate void Converter(void* source, void* target, int n);
    
    readonly static Dictionary<Type, Dictionary<Type, Converter>> Converters = new Dictionary<Type, Dictionary<Type, Converter>>();
    
    static DataConversion()
    {
        Converters.Add(typeof(int), new Dictionary<Type, Converter>()
            {
                [typeof(float)] = Convert_Int_Float
            }
        );
    }
    
    private unsafe static void Convert<TTo>(Array source, in Span<TTo> target)
    {
        GCHandle handle = GCHandle.Alloc(source, GCHandleType.Pinned);
    
        var sourceType = source.GetType().GetElementType();
    
        Converter converter = Converters[sourceType][typeof(TTo)];
        converter(handle.AddrOfPinnedObject().ToPointer(), Unsafe.AsPointer(ref target[0]), source.Length);
    
        handle.Free();
    }
    
    private static unsafe void Convert_Int_Float(void* source, void* target, int n)
    {
        var from = new ReadOnlySpan<int>(source, n);
        var to = new Span<float>(target, n);
    
        for (int i = 0; i < n; i++)
            to[i] = (float)from[i];
    }
}

My question is though if it is possible to do this without any unsafe code? The problem I have is that I cannot event make the converter methods (here: Convert_Int_Float) generic since then they wouldn't fit into the dictionary. My only option was to put it into the most generic version which was/is void *.

So my question is if there is a somewhat better approach?

0 Answers
Related