Search code examples
c#unsafe

Generic for unsafe: T[,] and T[,,] - C# 7.3+


I run c# 7.3 so please no comments like 'this will never work'

I have 2 Classes with a common interface: Image2D and Image3D and the Common Interface Image

public interface IImage<T, S> where T : unmanaged {
  long Length { get; }
  S Data { get; }
  T GetMaxTypeValue();
  T GetMinTypeValue(); 
}

public abstract class I2D<T> : IImage<T, T[,]> where T : unmanaged {
    public int LengthX { get; }
    public int LengthY { get; }

    public T[,] Data { get; }
    ...
}


public abstract class I3D<T> : IImage<T, T[,,]> where T : unmanaged {
    public int LengthX { get; }
    public int LengthY { get; }
    public int LengthZ { get; }

    public T[,,] Data { get; }
    ....
}

What I am trying to do now: No matter if the input is of type I3D or I2D i need unsafe access on the Data array...

public static unsafe T Smth<T, S>(IImage<T, S> image) where T : unmanaged where S : unmanaged {
    var data = image.Data;

    fixed (T* pData = data) {
        do someting
    }
    return ...
}

This, of course, does not work: 'Can not convert initializer type S to type T*' which makes sense... But how Can I get the datatype of the arrays? Is there a better way to solve this problem?


Solution

  • I wonder if the trick here is instead of S Data { get; }, to have (either as Data, or as a different property): ref T Data {get;}.

    Assuming you have underlying arrays, you should be able to use all the zeros to get this reference; so for the 2D case:

    public ref T Data => ref arr[0, 0];
    T[,] arr;
    

    and for the 3D case:

    public ref T Data => ref arr[0, 0, 0];
    T[,,] arr;
    

    Now you can use:

    ref var data = ref image.Data;
    fixed (T* pData = &data)
    {
    
    }
    

    Although frankly I suspect you can also do everything you need without unsafe - especially via MemoryMarshal and Unsafe (nuget)