为什么 Dart 是通过引用传递的?
Why Dart is acting as pass by reference?
我知道 dart 就像 Java 一样按值传递,但我不明白为什么在下面的示例中它表现得像按引用传递?
class A {
int y = 10;
}
class B {
List<A> list = [];
void add(A a) {
a.y = 20;
list.add(a);
}
void runtest() {
print(list[0].y);
}
}
void main() {
A a = A();
B b = B();
b.add(a);
print(a.y);
b.runtest();
}
结果会是
20
20
为什么在(b)中的方法(add)中改变了对象(a)的变量(y),它也在main()中改变了对象(a)的变量(y)?
我认为你的困惑来自于对什么是变量和参数以及按引用传递和按值传递的概念的误解。
Dart中的变量可以看作是对对象的引用。当您将此变量作为方法的参数提供时,您必须提供此引用的副本。因此,该方法不能更改此引用指向的对象。但是该方法确实可以访问对象本身,因为它获得了引用的副本。因此,当我们将变量赋予方法时,我们并没有创建对象的副本。
Dart 中的某些对象是不可变的(如 String、int、double 等),因此方法无法更改这些类型对象的内部状态。因此,即使我们获得了对其中一些对象的引用副本,我们也无法更改状态,因此我们看到了这种“按值传递”行为。
但是如果一个对象(例如List
)能够改变它的内部状态,我们可以进行修改,比如向List
对象添加项目。由于我们从不创建新对象,因此我们将在我们的方法之外看到这种变化,因为我们已经对对象本身进行了更改而没有对引用进行任何更改。这给出了您在示例中看到的这种“按引用传递”行为。
但这并没有改变变量本身是一个引用的事实,当作为参数提供给方法时,它总是“按值传递”。
一个小例子展示了当我们谈论方法的参数时我所说的“按值传递”的意思,可以在这里看到:
class A {
int value;
A(this.value);
}
void main() {
final a = A(1);
print(a.value); // 1
myMethod(a);
print(a.value); // 1
}
void myMethod(A a) {
a = A(2);
}
如示例所示,myMethod
中的 a
变量可以更改为指向另一个对象,但由于此变量是按值传递的,因此更改不会发生在 main
.
飞镖是 call-by-value。 值 是对象引用。有时也称为 "call by sharing" 或“按对象引用调用”。它是 call-by-value ... 用于特定的值选择。
对象具有身份,这是面向对象编程的基本基石之一。因此,您不能 复制 它们而不会成为新对象。这就是为什么面向对象的语言通常不将对象存储在变量中,它们存储对对象的引用。然后这些引用按值传递。
Call-by-value与call-by-reference的区别在于后者传递变量(也称为L-values,左边的东西赋值),而不是它们的值(R-values)。 Call-by-reference 允许您创建局部变量,通过引用将其传递给另一个函数,并让该函数更改绑定到该变量的值。 Dart 做不到,它只能传递变量的值。
对象可以包含变量(实例变量,又名字段),因此您可以传递对包含变量的对象的引用(按值),这允许函数改变 something 通过参数,但不能是局部变量。
我知道 dart 就像 Java 一样按值传递,但我不明白为什么在下面的示例中它表现得像按引用传递?
class A {
int y = 10;
}
class B {
List<A> list = [];
void add(A a) {
a.y = 20;
list.add(a);
}
void runtest() {
print(list[0].y);
}
}
void main() {
A a = A();
B b = B();
b.add(a);
print(a.y);
b.runtest();
}
结果会是
20
20
为什么在(b)中的方法(add)中改变了对象(a)的变量(y),它也在main()中改变了对象(a)的变量(y)?
我认为你的困惑来自于对什么是变量和参数以及按引用传递和按值传递的概念的误解。
Dart中的变量可以看作是对对象的引用。当您将此变量作为方法的参数提供时,您必须提供此引用的副本。因此,该方法不能更改此引用指向的对象。但是该方法确实可以访问对象本身,因为它获得了引用的副本。因此,当我们将变量赋予方法时,我们并没有创建对象的副本。
Dart 中的某些对象是不可变的(如 String、int、double 等),因此方法无法更改这些类型对象的内部状态。因此,即使我们获得了对其中一些对象的引用副本,我们也无法更改状态,因此我们看到了这种“按值传递”行为。
但是如果一个对象(例如List
)能够改变它的内部状态,我们可以进行修改,比如向List
对象添加项目。由于我们从不创建新对象,因此我们将在我们的方法之外看到这种变化,因为我们已经对对象本身进行了更改而没有对引用进行任何更改。这给出了您在示例中看到的这种“按引用传递”行为。
但这并没有改变变量本身是一个引用的事实,当作为参数提供给方法时,它总是“按值传递”。
一个小例子展示了当我们谈论方法的参数时我所说的“按值传递”的意思,可以在这里看到:
class A {
int value;
A(this.value);
}
void main() {
final a = A(1);
print(a.value); // 1
myMethod(a);
print(a.value); // 1
}
void myMethod(A a) {
a = A(2);
}
如示例所示,myMethod
中的 a
变量可以更改为指向另一个对象,但由于此变量是按值传递的,因此更改不会发生在 main
.
飞镖是 call-by-value。 值 是对象引用。有时也称为 "call by sharing" 或“按对象引用调用”。它是 call-by-value ... 用于特定的值选择。
对象具有身份,这是面向对象编程的基本基石之一。因此,您不能 复制 它们而不会成为新对象。这就是为什么面向对象的语言通常不将对象存储在变量中,它们存储对对象的引用。然后这些引用按值传递。
Call-by-value与call-by-reference的区别在于后者传递变量(也称为L-values,左边的东西赋值),而不是它们的值(R-values)。 Call-by-reference 允许您创建局部变量,通过引用将其传递给另一个函数,并让该函数更改绑定到该变量的值。 Dart 做不到,它只能传递变量的值。
对象可以包含变量(实例变量,又名字段),因此您可以传递对包含变量的对象的引用(按值),这允许函数改变 something 通过参数,但不能是局部变量。