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?