Арифметика IntPtr

Для текущего проекта мы думали о соответствующих мерах, которые могли быть приняты основным программным циклом. Основная программа принимает сообщения XML и сохраняет информацию в базу данных (с изрядным количеством обработки промежуточного).

  1. ошибки Данных, которые указывают на что-то не так вход. Соответствующие меры должны сохранить сообщение к каталогу журнала, но не обработать его.
  2. ошибки Инфраструктуры, которые указывают на некоторый субкомпонент (как входная очередь, база данных SQL, библиотека JNI) неправильно функционируют. Сон в течение нескольких минут затем снова соединяется.
  3. ошибки Конфигурации, которые указывают на некоторую конфигурацию аспекта, неосуществимы. Выйдите из программы.

первый объект является контролируемой исключительной ситуацией, так как мы полагали, что проверка данных была частью интерфейса метода. Другие неконтролируемы, так как основной цикл не может знать реализации субкомпонентов, например, реализация может использовать базу данных SQL или может просто сохранить данные в памяти - вызывающая сторона не должна знать.

7
задан Luca 27 August 2010 в 13:03
поделиться

4 ответа

У вас есть четыре варианта, которые я могу придумать: два с использованием только «безопасного» кода и два с использованием небезопасного кода. Небезопасные варианты, вероятно, будут значительно быстрее.

Безопасно:

  • Выделите свой массив в управляемой памяти, и объявите, что ваша функция P / Invoke принимает массив. т.е. вместо:

      [DllImport (...)]
    статический extern bool Foo (int count, IntPtr arrayPtr);
    

    сделать это

      [DllImport (...)]
    static extern bool Foo (int count, NativeType [] массив);
    

    (Я использовал NativeType для вашего имени структуры вместо T , поскольку T часто используется в общем контексте.)

    Проблема с этим подходом состоит в том, что, насколько я понимаю, массив NativeType [] будет маршалироваться дважды при каждом вызове Foo . Он будет скопирован из управляемой памяти в неуправляемую. память перед вызовом и копирование из неуправляемой памяти в управляемую память впоследствии. Однако это можно улучшить, если Foo будет только читать или записывать в массив. В этом случае украсьте параметр tarray атрибутом [In] (только для чтения) или [Out] (только для записи). Это позволяет среде выполнения пропустить один из этапов копирования.

  • Как и сейчас, выделите массив в неуправляемой памяти и используйте несколько вызовов Marshal.PtrToStructure и Marshal .StructureToPtr . Это, вероятно, будет работать даже хуже, чем первый вариант, поскольку вам все равно нужно копировать элементы массива туда и обратно, и вы делаете это поэтапно, поэтому у вас больше накладных расходов. С другой стороны, если у вас много элементов в массиве, но вы обращаетесь к небольшому их количеству между вызовами Foo , тогда это может работать лучше. Вам может понадобиться пара небольших вспомогательных функций, например:

     static T ReadFromArray  (IntPtr arrayPtr, int index) {
     // ниже, если вы ** знаете **, что будете на 32-битной платформе,
     // вы можете изменить ToInt64 () на ToInt32 ().
     return (T) Marshal.PtrToStructure ((IntPtr) (arrayPtr.ToInt64 () +
     index * Marshal.SizeOf (typeof (T)));
    }
    // вы можете изменить `T value` ниже на` ref T value`, чтобы избежать еще одной копии
    static void WriteToArray  (IntPtr arrayPtr, int index, T value) {
     // ниже, если вы ** знаете **, что будете на 32-битной платформе,
     // вы можете изменить ToInt64 () на ToInt32 ().
     Marshal.StructureToPtr (значение, (IntPtr) (arrayPtr.ToInt64 () +
     index * Marshal.SizeOf (typeof (T)), ложь);
    }
    

Небезопасно:

  • Разместите свой массив в неуправляемой памяти и используйте указатели для доступа к элементам. Это означает, что весь код, использующий массив, должен находиться в пределах блока unsafe .

     IntPtr arrayPtr = Marhsal.AllocHGlobal (count * sizeof (typeof (NativeType)));
    unsafe {
     NativeType * ptr = (NativeType *) arrayPtr.ToPointer ();
    
     ptr [0] .Member1 = foo;
     ptr [1] .Member2 = bar;
     /* и так далее */
    }
    Foo (количество, arrayPtr);
    
  • Разместите свой массив в управляемой памяти и закрепите его, когда вам нужно вызвать собственную процедуру:

     NativeType [] array = new NativeType [count];
    массив [0] .Member1 = foo;
    array [1] .Member2 = bar;
    /* и так далее */
    
    unsafe {
     фиксированный (NativeType * ptr = array)
     Foo (количество, (IntPtr) ptr);
     // или просто Foo (count, ptr), если Foo объявлен как таковой:
     // статический небезопасный тип bool Foo (int count, NativeType * arrayPtr);
    }
    

Этот последний вариант, вероятно, самый чистый, если вы можете использовать небезопасный код и беспокоиться о производительности, потому что ваш единственный небезопасный код - это то, где вы вызываете собственную процедуру. Если производительность не является проблемой (возможно, если размер массива относительно мал) или если вы не можете использовать небезопасный код (возможно, у вас нет полного доверия), то первый вариант, вероятно, будет самым чистым, хотя как я уже упоминал, если количество элементов, к которым вы будете обращаться между вызовами собственной подпрограммы, составляет небольшой процент от количества элементов в массиве, тогда второй вариант будет быстрее.

Примечание:

Небезопасный операции предполагают, что ваша структура непреобразуемая . Если нет, то безопасные процедуры - ваш единственный выход.

11
ответ дан 6 December 2019 в 07:27
поделиться

Действительно, тип IntPtr не имеет собственных арифметических операторов. Правильная (небезопасная) арифметика указателей поддерживается в C #, но IntPtr и класс Marshal существуют для «более безопасного» использования указателей.

Я думаю, вы хотите что-то вроде следующего:

int index = 1; // 2nd element of array
var v = (T)Marshal.PtrToStructure(new IntPtr(data.ToInt32() + 
    index * Marshal.SizeOf(typeof(T)), typeof(T));

Также обратите внимание, что IntPtr не имеет неявного преобразования между int и IntPtr , так что здесь не повезло.

В общем случае. , если вы собираетесь делать что-то удаленно сложное с указателями, вероятно, лучше выбрать небезопасный код.

3
ответ дан 6 December 2019 в 07:27
поделиться

Вы можете использовать адрес интегральной памяти структуры указателя, используя IntPtr.ToInt32 () , но остерегайтесь "битности" платформы (32/64).

Для типичная арифметика указателей, используйте указатели (найдите в документации fixed и unsafe ):

T data = new T[count];
fixed (T* ptr = &data)
{
    for (int i = 0; i < count; i++)
    {
        // now you can use *ptr + i or ptr[i]
    }
}

EDIT:

Я думаю, что IntPtr позволяет вы обрабатываете указатели на данные без явного манипулирования адресами указателей . Это позволяет взаимодействовать с COM и собственным кодом без объявления небезопасных контекстов. Единственное требование, которое предъявляет среда выполнения, - это разрешение на неуправляемый код. Для этих целей кажется, что большинство методов маршалинга принимают только целые IntPtr данные, а не чистые целочисленные или длинные типы, поскольку он обеспечивает тонкий слой, защищающий от манипулирования содержимым структуры. Вы можете напрямую манипулировать внутренними компонентами IntPtr , но для этого либо требуются небезопасные указатели (опять же небезопасные контексты), либо отражение. Наконец, IntPtr автоматически адаптируется к размеру указателя платформы.

2
ответ дан 6 December 2019 в 07:27
поделиться

«Почему IntPtr не хватает арифметики?»

IntPtr хранит только адрес памяти. У него нет никакой информации о содержимом этой ячейки памяти. Таким образом, он похож на void * . Чтобы включить арифметику указателей, вы должны знать размер объекта, на который указывает.


По сути, IntPtr в первую очередь предназначен для использования в управляемых контекстах в качестве непрозрачного дескриптора (т. Е. Такого дескриптора, который вы не разыменовываете напрямую в управляемом коде и просто оставляете его для перехода к неуправляемому коду). небезопасный контекст предоставляет указатели, которыми вы можете управлять напрямую.

8
ответ дан 6 December 2019 в 07:27
поделиться
Другие вопросы по тегам:

Похожие вопросы: