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