结构在传递给方法时不通过引用传递
Struct is not passed by reference when passed to a method
struct Data {
public int x;
}
void change_x(Data data) {
data.x = 123;
}
Data a = Data();
change_x(a);
print("%d", a.x); // 0
但是文档说:
when a struct type instance is passed to a method, a copy is not made. Instead a reference to the instance is passed.
- in https://wiki.gnome.org/Projects/Vala/Manual/Types
怎么了?
我认为您引用的引用文本已经过时或开头是错误的。
如果您希望通过引用传递它,则必须使用 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 多个值的一种方式,但在此示例中, 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 中与其他值类型的行为相同,但如果您通过使用带有 valac
的 --ccode
开关查看 C 代码,您会看到结构被复制并传递引用。当您需要了解 Vala 如何维护 C ABI(应用程序二进制接口)时,这是相关的。
结构方法
您对手册的引用可能不清楚,但我认为它与结构中的方法有关。如果 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
该方法现在在实例上运行。
[简单类型] 结构
您引用的手册中的部分还在下一句中指出:
This behaviour can be changed by declaring the struct to be a simple
type.
为了完整起见,这里是最后一个示例:
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
。如果您想要定义一个作用于简单类型结构实例的结构方法,则必须将该方法定义为 return 一个包含修改后的值的新实例。
结论
结构是 Vala 中的值类型,但了解它们的实现方式有助于理解与 C ABI 的兼容性。在您的示例中,结构表现为值类型,但根据 C ABI 进行复制和引用传递。引用似乎与结构中定义的方法最相关。
struct Data {
public int x;
}
void change_x(Data data) {
data.x = 123;
}
Data a = Data();
change_x(a);
print("%d", a.x); // 0
但是文档说:
when a struct type instance is passed to a method, a copy is not made. Instead a reference to the instance is passed.
- in https://wiki.gnome.org/Projects/Vala/Manual/Types
怎么了?
我认为您引用的引用文本已经过时或开头是错误的。
如果您希望通过引用传递它,则必须使用 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 多个值的一种方式,但在此示例中, 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 中与其他值类型的行为相同,但如果您通过使用带有 valac
的 --ccode
开关查看 C 代码,您会看到结构被复制并传递引用。当您需要了解 Vala 如何维护 C ABI(应用程序二进制接口)时,这是相关的。
结构方法
您对手册的引用可能不清楚,但我认为它与结构中的方法有关。如果 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
该方法现在在实例上运行。
[简单类型] 结构
您引用的手册中的部分还在下一句中指出:
This behaviour can be changed by declaring the struct to be a simple type.
为了完整起见,这里是最后一个示例:
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
。如果您想要定义一个作用于简单类型结构实例的结构方法,则必须将该方法定义为 return 一个包含修改后的值的新实例。
结论
结构是 Vala 中的值类型,但了解它们的实现方式有助于理解与 C ABI 的兼容性。在您的示例中,结构表现为值类型,但根据 C ABI 进行复制和引用传递。引用似乎与结构中定义的方法最相关。