与 C++ 相比,C# 中的按引用传递的工作方式是否正确?

Am I correct about how pass-by-reference in C# works compared to C++?

本文关键字:是否 方式 工作 按引用传递 相比 C++      更新时间:2023-10-16

在C++中,如果我没记错的话,函数的任何参数都是按值传递的,除非由指示通过引用传递的&限定。

例如

int double ( int i ) 
{
int twice = i * i;
return twice; 
}
int main ( )
{
int i = 4;
int j = double(i);
return 0;
}

相当于

int i = 4; 
// --- here's the part where the function runs ---
int temp = i; 
int twice = temp * temp;
i = twice;
// -----------------------------------------------

它在 C# 中的工作方式相同,因为int数据类型值类型(我听说过这两个术语都使用了)。

事情变得不同的是,当你传入在 C# 中称为引用类型对象的内容时。在C++中,如果您在没有限定的情况下传入class&,则该函数的工作方式就像它使用参数的深层副本一样。

例如,C++ 中的以下内容不会递增CharlieAge

class Dude
{
pulic Dude ( name = "", age = 0 ) : Name(name), Age(age) { }
public string Name; 
public int Age;
}
public void IncrementDudeAge ( Dude dude )
{
++dude.Age;  
}
int main ( )
{
Dude Charlie = new Dude ( "Charlie", 69 );
IncrementDudeAge(Charlie);
std::cout << Charlie.Age; // prints 69
}

main相当于

Dude Charlie = new Dude ( "Charlie", 69 );
Dude temp = new Dude( "Charlie", 69 );
++temp.Age;
std::cout << Charlie.Age; // prints 69

这与 C# 不同,因为在 C# 中,相同的代码会递增Age

public class Program
{
public static void IncrementDudeAge ( Dude dude )
{
++dude.Age;
}
public class Dude
{
public string Name { get; set; }
public int Age { get; set; }
}
public static void Main ()
{
Dude Charlie = new Dude() { Name = "Charlie", Age = 69 };
IncrementDudeAge(Charlie);
Console.WriteLine(Charlie.Age); // prints 70
}
}

这段代码相当于C++代码,例如

class Dude
{
pulic Dude ( name = "", age = 0 ) : Name(name), Age(age) { }
public string Name; 
public int Age;
}
public void IncrementDudeAge ( Dude * dude )
{
++*dude.Age;  
}
int main ( )
{
Dude Charlie = new Dude ( "Charlie", 69 );
IncrementDudeAge(&Charlie);
std::cout << Charlie.Age; // prints 70
}     

换句话说,在上述 2 段代码中,您正在传递一个别名Charlie,指向Charlie的东西的副本。在 C++ 中,该别名是实际的整型类型,而在 C# 中,您可以将其视为别名。

因为在这两种情况下,它都是指向Charlie,如果您尝试重新分配他,则不会影响实际Charlie

public void IncrementDudeAge ( Dude * dude )
{
dude = new Dude ( "Charlie", 70 ); 
}

public static void IncrementDudeAge ( Dude dude )
{
dude = new Dude() { Name = "Charlie", Age = 70 };
}

不会做任何事情。这就是需要在 C# 中使用ref,或者在 C++ 中使用***&的地方。

我对一切都正确吗?

tl;大卫:是的。

我对C++不是很熟悉,但是AFAICS,你的解释看起来是正确的。

在 C 中♯,需要考虑两个独立的轴:

  • 按值传递与按引用传递
  • 值类型与引用类型

请注意,"参考"一词使用了两次,上面有两种不同的含义。如果我们想删除这个词,我们可以大致重写如下:

  • 值传递与按绑定传递
  • 值类型与指针类型

按值传递是 C♯ 中的默认值。如果您不执行任何特殊操作,则参数传递始终是按值传递的,没有例外。如果使用ref关键字显式批注定义和调用站点,则可以请求按引用传递。(还有out关键字,它是一种特殊的按引用传递,用于输出参数,其中不允许被调用方取消引用,并且需要在每个可能的返回路径中初始化引用。

因此,按值传递和按引用传递之间的区别在于参数的传递方式

值类型和引用类型之间的区别在于传递的内容:对于值类型,值本身被传递,对于引用类型,传递指向值的指针。由于引用类型始终与指针一起使用,而值类型始终不使用指针,因此没有用于引用或取消引用指针的特殊语法,所有这些都是隐式的。但它就在那里。

这为我们提供了四种可能的组合:

struct MutableCell
{
public string value;
}
class Program
{
static void ArgumentPassing(string[] foo, MutableCell bar, ref string baz, ref MutableCell qux)
{
foo[0] = "More precisely, for reference types it is call-by-object-sharing, which is a special case of pass-by-value.";
foo = new string[] { "C# is not pass-by-reference." };
bar.value = "For value types, it is *not* call-by-sharing.";
bar = new MutableCell { value = "And also not pass-by-reference." };
baz = "It also supports pass-by-reference if explicitly requested.";
qux = new MutableCell { value = "Pass-by-reference is supported for value types as well." };
}
static void Main(string[] args)
{
var quux = new string[] { "Yes, of course, C# *is* pass-by-value!" };
var corge = new MutableCell { value = "For value types it is pure pass-by-value." };
var grault = "This string will vanish because of pass-by-reference.";
var garply = new MutableCell { value = "This string will vanish because of pass-by-reference." };
ArgumentPassing(quux, corge, ref grault, ref garply);
Console.WriteLine(quux[0]);
// More precisely, for reference types it is call-by-object-sharing, which is a special case of pass-by-value.
Console.WriteLine(corge.value);
// For value types it is pure pass-by-value.
Console.WriteLine(grault);
// It also supports pass-by-reference if explicitly requested.
Console.WriteLine(garply.value);
// Pass-by-reference is supported for value types as well.
}
}

是的,你是对的。可以指出一些微小的措辞问题,但所有的意见都是可以的。

相关文章: