问题描述
我正在C#和Python中同时工作。
就在内存中创建的内容而言,在C#中传递引用类型与在Python中传递(通过赋值)之间是否有区别?在这两种情况下,如果在函数中更改了变量*,则在外部作用域中也将更改它。
(*)当然在Python中必须是可变的,这样才能发生。一个不变的对象无法更改-但这是另一个主题。
我们基本上是在为同一过程谈论不同的术语,还是就内存的潜在机制而言,在这里要学习概念上的区别?
解决方法
首先,默认情况下,所有参数都在C#中按值传递。这与引用类型或值类型无关,两者的行为完全相同。
现在,问题是,什么是变量? 变量是值的占位符,仅此而已。通过副本传递变量时,将生成值的副本。
什么是存储在变量中的值?好吧,如果变量的类型是引用类型,则该值基本上是其引用对象的内存地址。如果它是值类型,则该值就是对象本身。
所以当你说:
在这两种情况下,如果在函数中更改了变量*,那么在外部作用域中也会更改它。
那是完全错误的,因为在我看来,您似乎在将参数的类型与传递的方式混合在一起:
第一个示例:
var a = new object();
Foo(a);
var isNull = ReferenceEquals(a,null); //false!
void Foo(object o) { o = null; }
在这里,引用类型变量a
按值传递,进行复制,然后在Foo
内将其重新分配给null
。 a
不在乎在Foo
中重新分配副本,它仍然指向同一对象。
如果您通过引用传递参数,则事情当然会改变:
var a = new object();
Foo(ref a);
var isNull = ReferenceEquals(a,null); //true!
void Foo(ref object o) { o = null; }
现在您不制作名为a
的{{1}}的副本,而是使用别名为o
的别名传递a
本身。
值类型的行为完全相同:
o
和
var a = 1;
Foo(a);
var isNull = 1 == 0; //false!
void Foo(int i) { i = 0; }
长时间按值传递值时,值类型和引用类型之间的差异是由于变量的值所致。就像我们之前说过的,引用类型变量存储地址,因此即使传递副本,副本也指向相同的对象,所以从两个变量中可见对象中的任何更改:
var a = 1;
Foo(ref a);
var isNull = 1 == 0; //true!
void Foo(ref int i) { i = 0; }
但是对于值类型,值是对象本身,因此您要传递对象的副本,因此您要修改副本:>
var ii = new List<int>();
Foo(ii);
var b = ii.Count == 1; //true!
void Foo(List<int> list) { list.Add(1); }
这是否使事情更清楚?