#c# #pointers #unmanaged #typehandler
#c# #указатели #неуправляемый #обработчик типов
Вопрос:
Это довольно сложный вопрос, так что потерпите меня.
Я определил структуру, которая служит в качестве неуправляемого массива. Поскольку я использую (и придерживаюсь) C # 7.3, универсальные структуры с неуправляемым ограничением рассматриваются как управляемый тип. Это означает, что я не могу принимать или использовать указатели на эту структуру. По этой причине неуправляемый массив не объявлен универсальным. Чтобы обеспечить безопасность типов, при вызове (псевдо-) конструктора дескриптор типа универсального типа сохраняется вместе с объектом. Для каждого последующего вызова дескриптор общего типа сравнивается с сохраненным дескриптором типа, чтобы утверждать, что это тот же тип.
Затем я обернул этот неуправляемый массив в другой универсальный объект. Это серьезно упрощает вызовы API для неуправляемого массива. Поскольку вы не можете видеть, что хранится в указателе, я создал DebuggerTypeProxy, который преобразует неуправляемый массив в управляемый массив. Таким образом, во время отладки вы можете просмотреть содержимое неуправляемого массива.
Теперь вот где (наконец) возникает проблема. При определении этого DebuggerTypeProxy как класса возникает несоответствие между дескрипторами типов для вызова неуправляемого массива. Однако при определении DebuggerRypeProxy как структуры несоответствия нет. Что именно здесь происходит?
Ниже приведен некоторый код, используемый для реализации, чтобы пояснить, что я сделал. Он сильно урезан, чтобы показать только соответствующие части.
public unsafe struct UnsafeArray
{
void* _buffer;
int _length;
IntPtr _typeHandle;
//Pseudo-constructor
public static UnsafeArray* Allocate<T>(int size) where T : unmanaged
{
//Do a lot of allocation stuff...
UnsafeArray* array;
//Storing the typehandle of the type that is used to construct the unmanaged array
array->_typeHandle = typeof(T).TypeHandle.Value;
return array;
}
//One of the methods that operates on the UnsafeArray
public static T* GetPtr<T>(UnsafeArray* array, long index) where T : unmanaged
{
UDebug.Assert(array != null);
//This assertion fails!!
UDebug.Assert(typeof(T).TypeHandle.Value == array->_typeHandle);
// cast to uint trick, which eliminates < 0 check
if ((uint)index >= (uint)array->_length)
{
throw new IndexOutOfRangeException(index.ToString());
}
return (T*)array->_buffer index;
}
}
Ниже приведена оболочка, которая использует NativeArray внутри.
[DebuggerDisplay("Length = {Length}")]
[DebuggerTypeProxy(typeof(Debug.TypeProxies.NativeArrayDebugView<>))]
public unsafe struct NativeArray<T> : IDisposable, IEnumerable<T>, IEnumerable where T : unmanaged
{
private UnsafeArray* m_inner;
public T[] ToArray()
{
var arr = new T[Length];
Copy(this, 0, arr, 0, arr.Length);
return arr;
}
//A lot of other stuff....
}
Наконец, тип, используемый для DebuggerTypeProxy
internal struct NativeArrayDebugView<T> where T : unmanaged
{
private readonly NativeArray<T> m_array;
public NativeArrayDebugView(NativeArray<T> array)
{
m_array = array;
}
public T[] Items
{
get
{
if (!m_array.IsCreated)
throw new System.NullReferenceException();
//This fails as it makes an internal call to the UnsafeArray with the wrong type!
return m_array.ToArray();
}
}
}
Наконец, стоит упомянуть, что NativeArrayDebugView отлично работает при его создании вручную и просмотре его содержимого. Он нарушается только при следующих условиях:
- NativeArrayDebugView является классом
- Доступ к NativeArrayDebugView осуществляется через представление отладки при просмотре NativeArray
Я понимаю, что это очень длинный и, вероятно, сложный вопрос, поэтому, если кому-то нужны разъяснения, я бы с радостью попытался их предоставить.