Маршалинг данных между управляемым и неуправляемым кодом - Ключевые слова 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 & |