Подтвердить что ты не робот

В чем разница между [In, Out] и ref при использовании pinvoke в С#?

Есть ли разница между использованием [In, Out] и просто использованием ref при передаче параметров с С# на С++?

Я нашел пару разных сообщений SO, а также некоторые материалы из MSDN, которые приближаются к моему вопросу, но не совсем отвечают на него. Я предполагаю, что я могу безопасно использовать ref так же, как я бы использовал [In, Out], и что маршаллер не будет действовать иначе. Меня беспокоит то, что он будет действовать по-другому, и что С++ не будет удовлетворен передачей моей С# -структуры. Я видел обе вещи в базе кода, в которой я работаю...

Вот сообщения, которые я нашел и читал через:

Являются ли атрибуты P/Invoke [In, Out] необязательными для массивов маршалинга? Заставляет меня думать, что я должен использовать [In, Out].

Эти три сообщения заставляют меня думать, что я должен использовать [In, Out], но я могу использовать ref вместо этого, и он будет иметь тот же машинный код. Это заставляет меня думать, что я ошибаюсь, поэтому прошу здесь.

4b9b3361

Ответ 1

Использование ref или out произвольно не. Если для нативного кода требуется pass-by-reference (указатель), вы должны использовать эти ключевые слова, если тип параметра является типом значения. Так что джиттер знает, чтобы создать указатель на значение. И вы должны опустить их, если тип параметра является ссылочным типом (классом), объекты уже указатели под капотом.

Затем атрибуты [In] и [Out] необходимы для устранения неоднозначности указателей, они не указывают поток данных. [In] всегда подразумевается маршеллером pinvoke, поэтому не нужно указывать явно. Но вы должны использовать [Out], если вы ожидаете увидеть какие-либо изменения, внесенные собственным кодом, в структуру или член класса обратно в ваш код. Маршрутизатор pinvoke избегает автоматического копирования, чтобы избежать затрат.

Далее возникает вопрос о том, что [Out] часто не требуется. Бывает, когда значение является blittable, дорогим словом, которое означает, что управляемое значение или макет объекта идентичны исходному макету. Маршрутизатор pinvoke может взять ярлык, привязать объект и передать указатель на хранилище управляемых объектов. Вы неизбежно увидите изменения, так как собственный код напрямую модифицирует управляемый объект.

Что-то, что вы в целом сильно хотите преследовать, это очень эффективно. Вы помогаете, указав тип атрибута [StructLayout (LayoutKind.Sequential)], он подавляет оптимизацию, которую CLR использует для переупорядочения полей, чтобы получить наименьший объект. И используя только поля простых типов значений или буферов фиксированного размера, хотя у вас часто нет такого выбора. Никогда не используйте bool, вместо этого используйте байт. Нет простого способа узнать, является ли тип blittable, отличным от того, работает он неправильно или с помощью отладчика и сравнивает значения указателя.

Просто будьте явным и всегда используйте [Out], когда вам это нужно. Это ничего не стоит, если не получилось. И это самодокументируется. И вы можете чувствовать себя хорошо, что он по-прежнему будет работать, если изменится архитектура собственного кода.