通过引用还是通过分配? C#与Python

问题描述

我正在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内将其重新分配给nulla不在乎在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); } 

这是否使事情更清楚?