Структура не передается по ссылке при передаче в метод
struct Data {
public int x;
}
void change_x(Data data) {
data.x = 123;
}
Data a = Data();
change_x(a);
print("%d", a.x); // 0
но в документе говорится:
когда экземпляр типа структуры передается методу, копия не создается. Вместо этого передается ссылка на экземпляр.
- в https://wiki.gnome.org/Projects/Vala/Manual/Types
Что случилось?
2 ответа
Я думаю, что цитируемый текст, на который вы ссылаетесь, либо устарел, либо был неправильным с самого начала.
Вы должны использовать ref
(или же out
) если вы хотите, чтобы он был передан по ссылке (отсюда и название ref
).
struct Data {
public int x;
}
void change_x (ref Data data) {
data.x = 123;
}
int main () {
Data a = Data ();
change_x (ref a);
print ("%d\n", a.x);
return 0;
}
Структуры в Vala реализованы как копии по назначению и передаются по ссылке. Таким образом, вы можете думать о своем примере как о копировании структуры, потому что она присваивается параметру в функции, а затем эта копия передается по ссылке. Это то, что происходит за кулисами в сгенерированном C-коде, но со стороны Vala это означает, что структура является типом значения. Только при взаимодействии с библиотекой C полезно знать, что копия структуры передается по ссылке. Цитата из руководства относится к методам структуры, но прежде чем мы рассмотрим это подробно, давайте немного разберемся со значениями и ссылочными типами.
Vala, как и Java, C# и многие другие языки, имеет два типа типов данных: типы значений и ссылочные типы.
Типы значений передаются по значению
Когда тип значения передается в качестве аргумента функции или методу, тогда значение передается в качестве аргумента, но это копия значения. Если функция или метод продолжают модифицировать полученный параметр, это не изменит значение в вызывающем коде. Код инкапсулирован.
Следующий пример:
void main () {
int a = 23;
print ("Initial value: %i\n", a);
modify_example (a);
print ("Final value: %i\n", a);
}
void modify_example (int x) {
x += 100;
}
производит:
Initial value: 23
Final value: 23
Хотя значение изменяется в функции, оно также не изменяет значение из вызывающего кода.
Типы значений могут быть переданы по ссылке
Вместо того, чтобы передавать значение в функцию или метод, использование ref
Ключевое слово передаст ссылку на значение. Это создает псевдоним для вызывающего значения. Результатом являются два идентификатора для одной и той же ячейки памяти.
Просто добавив ref
Ключевое слово следующий пример:
void main () {
int a = 23;
print ("Initial value: %i\n", a);
modify_example (ref a);
print ("Final value: %i\n", a);
}
void modify_example (ref int x) {
x += 100;
}
сейчас производит:
Initial value: 23
Final value: 123
По телефону modify_example ()
побочным эффектом является также изменение значения в коде вызова. Использование ref
делает это явным и может использоваться в качестве способа для функции возвращать несколько значений, но в этом примере было бы более понятным return
измененное значение вместо передачи по ссылке.
Типы ссылок всегда передаются по ссылке
Объекты являются ссылочными типами. Этот пример не использует явный ref
, но значение изменяется в вызывающем коде:
void main () {
var a = new ExampleReferenceType (23);
print ("Initial value: %i\n", a.value);
modify_example (a);
print ("Final value: %i\n", a.value);
}
class ExampleReferenceType {
public int value;
public ExampleReferenceType (int default = 0) {
this.value = default;
}
}
void modify_example (ExampleReferenceType x) {
x.value += 100;
}
Это производит:
Initial value: 23
Final value: 123
Объекты, измененные таким образом, могут вызвать проблемы при поиске ошибки. Это является преимуществом создания неизменных объектов стоимости. Это можно сделать, только установив значение в конструкторе, сделав все поля приватными и используя только свойство для получения значения, но не устанавливая его.
Структуры как типы значений
Следующий код:
void main () {
ExampleStruct a = { 23 };
print ("Initial value: %i\n", a.value);
modify_example (a);
print ("Final value: %i\n", a.value);
}
private struct ExampleStruct {
public int value;
}
void modify_example (ExampleStruct x) {
x.value += 100;
}
похож на ваш код и производит:
Initial value: 23
Final value: 23
Это то же поведение в Vala, что и другие типы значений, но если вы посмотрите на код C с помощью --ccode
переключаться с valac
вы увидите, что структура скопирована и передана по ссылке. Это актуально, когда вам нужно понять, как Vala поддерживает C ABI (Application Binary Interface).
Структурные методы
Ссылка, которую вы делаете на руководство, может быть неясной, но я думаю, что она относится к методам внутри структур. Если modify_example
перемещается внутри определения структуры:
void main () {
ExampleStruct a = { 23 };
print ("Initial value: %i\n", a.value);
a.modify_example ();
print ("Final value: %i\n", a.value);
}
private struct ExampleStruct {
public int value;
public void modify_example () {
this.value += 100;
}
}
это теперь производит:
Initial value: 23
Final value: 123
Теперь метод работает с экземпляром.
[SimpleType] Структуры
Раздел цитируемого вами руководства также гласит следующее предложение:
Это поведение можно изменить, объявив структуру простым типом.
Итак, для полноты вот последний пример:
void main () {
ExampleStruct a = { 23 };
print ("Initial value: %i\n", a.value);
a.modify_example ();
print ("Final value: %i\n", a.value);
}
[SimpleType]
private struct ExampleStruct {
public int value;
public void modify_example () {
this.value += 100;
}
}
и это производит:
Initial value: 23
Final value: 23
Хотя метод все еще определен в структуре, экземпляр передается по значению, а не по ссылке.
Простые структуры типов - это, например, определение основных типов значений в Vala. int
а также int64
, Если вы хотите определить метод struct, который действует на экземпляр структуры простого типа, тогда необходимо будет определить метод, чтобы он возвращал новый экземпляр, содержащий измененное значение.
Заключение
Структуры являются типами значений в Vala, но полезно знать, как они реализованы, чтобы понять совместимость с C ABI. В вашем примере структура ведет себя как тип значения, но копируется и передается по ссылке в терминах C ABI. Цитата кажется наиболее подходящей для методов, определенных в структурах.