传递给方法时,Struct不会通过引用传递

时间:2016-10-22 16:25:09

标签: struct vala

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 个答案:

答案 0 :(得分:3)

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修改后的值将更清晰,而不是通过引用传递。 / p>

参考类型始终通过参考传递

对象是引用类型。此示例不使用显式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中的行为与其他值类型相同,但如果您使用带有--ccode的{​​{1}}开关查看C代码,您将看到结构被复制并通过引用传递。当您需要了解Vala如何维护C ABI(应用程序二进制接口)时,这是相关的。

结构方法

您对手册的参考可能不明确,但我认为它与结构内的方法有关。如果valac在结构定义中移动:

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;
        }
}

该方法现在可以在实例上运行。

[SimpleType] Structs

您引用的手册中的部分也在下一句中说明:

  

可以通过将结构声明为简单来更改此行为   类型。

因此,为了完整起见,这是最后一个例子:

Initial value: 23
Final value: 123

这会产生:

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;
        }
}

虽然该方法仍在结构中定义,但实例是通过值而不是引用传递的。

简单类型结构是Vala中定义基本值类型的方式,例如Initial value: 23 Final value: 23 int。如果您希望定义的struct方法作用于简单类型的struct实例,则必须定义该方法以返回包含修改后的值的新实例。

结论

结构是Vala中的值类型,但了解如何实现它们以了解与C ABI的兼容性非常有用。在您的示例中,struct表现为值类型,但是根据C ABI通过引用进行复制和传递。引用似乎与结构中定义的方法最相关。

答案 1 :(得分:1)

我认为您所引用的引用文字要么已过时,要么以错误开头。

如果您希望通过引用传递它,则必须使用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;
}