Маршалинг данных между управляемым и неуправляемым кодом - Ключевые слова out и ref. Передача по ссылке
ОГЛАВЛЕНИЕ
Ключевые слова out и ref. Передача по ссылке
Выше отмечалось, что ключевые слова out и ref в языке C# можно напрямую сопоставить атрибутам [InAttribute] и [OutAttribute]. Ключевые слова out и ref также могут определять, в какой тип данных (или из какого типа данных) будет осуществляться маршалинг в среде CLR. Передача данных с ключевыми словами out или ref — это то же самое, что и передача по ссылке. Если вы обратите внимание на соответствующую подпись функции в промежуточном языке (IL) в ILDASM, вы обнаружите рядом с типом знак амперсанда (&) — это означает, что аргумент должен передаваться по ссылке. При передаче данных по ссылке среда CLR добавляет еще один уровень косвенности. На рис. 4 приведено несколько примеров.
Figure 4 Marshaling Results| Сигнатура C# | Неуправляемая сигнатура | Сигнатура MSIL | Сигнатура MSIL, воспринимаемая средой CLR |
| Базовые типы | |||
| int arg | int arg | int | [in] int |
| out int arg | int *arg | [out] int & | [out] int & |
| ref int arg | int *arg | int & | [in, out] int & |
| Структуры | |||
| MyStruct arg | MyStruct arg | MyStruct | [in] MyStruct |
| out MyStruct arg | MyStruct *arg | [out] MyStruct & | [out] MyStruct & |
| ref MyStruct arg | MyStruct *arg | MyStruct & | [in, out] MyStruct & |
| Строки | |||
| string arg | char *arg | string | [in] string |
| out string arg | char **arg | [out] string & | [out] string & |
| ref string arg | char **arg | string & | [in, out] string & |
| Классы | |||
| MyClass arg | MyClass *arg | MyClass | [in] MyClass |
| out MyClass arg | MyClass **arg | [out] MyClass & | [out] MyClass & |
| ref MyClass arg | MyClass **arg | MyClass & | [in, out] Myclass & |
Все сказанное о ключевых словах out и ref мы объединили в таблицу, приведенную на рис. 5.
Figure 5 Default Attributes| Сигнатура C# | Сигнатура MSIL | Атрибуты направления, используемые по умолчанию |
|---|---|---|
| <type> | type | [InAttribute] |
| out <type> | [OutAttribute] type & | [OutAttribute] |
| ref <type> | type & | [InAttribute, OutAttribute] |
Следует отметить, что если при передаче по ссылке не указать атрибуты направления, среда CLR будет использовать атрибуты [InAttribute] и [OutAttribute] автоматически, и именно поэтому в сигнатуре на рис. 4 стоит только «string &». Если указать хотя бы один из атрибутов, среда CLR будет использовать указанные значения (как показано в примере ниже), и стандартного поведения мы не увидим.
public static extern void
PassPointerToComplexStructure(
[In]ref ComplexStructure
pStructure);
Приведенная выше сигнатура преимущество перед стандартным поведением при наличии ключевого слова ref, поэтому используется только атрибут [InAttribute]. В данном примере при выполнении вызова P/Invoke указатель на ComplexStructure (это тип значений) передается из среды CLR в машинный код, но вызываемая сторона не может передать изменения обратно в тип ComplexStructure, к которому отсылает указатель pStructure. На рис. 6 приведены другие варианты сочетания атрибутов направления и ключевых слов.
Figure 6 More Attributes and Keywords| Сигнатура C# | Неуправляемая сигнатура IDL | Сигнатура MSIL |
| Out | ||
| [InAttribute] out int arg | Ошибка компиляции CS0036. Выходной параметр не может иметь атрибут In. | — |
| [OutAttribute] out int arg | [out] int *arg | [out] int & |
| [InAttribute, OutAttribute] out int arg | Ошибка компиляции CS0036. Выходной параметр не может иметь атрибут In. | — |
| Ref | ||
| [InAttribute] ref int arg | [in] int *arg | [in] int & |
| [OutAttribute] ref int arg | Ошибка компиляции CS0662. Невозможно указать для параметра ref только атрибут Out. Используйте оба атрибута (In и Out) или ни одного из них. | — |
| [InAttribute, OutAttribute] ref int arg | [in, out] int *arg | [in] [out] int & |
