与 C++ 相比,C# 中的按引用传递的工作方式是否正确?
Am I correct about how pass-by-reference in C# works compared to C++?
在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++ 中的以下内容不会递增Charlie
的Age
。
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.
}
}
是的,你是对的。可以指出一些微小的措辞问题,但所有的意见都是可以的。
- 不同/较旧的处理器运行c++代码的方式是否不同
- 并发/多线程:是否可以以这种方式生成相同的输出?
- 这种方式是否可以接受向向量添加unique_ptr?
- 在线程中读取无符号整数时,c++ 位是否以原子方式切换?
- 在 Eclipse 中添加库的工作方式是否与在 Visual Studio 中相同?
- VS2017 是否更改了 C++ 中访问 C# 命名空间的方式?
- 是否可以使用非常量指针调用非常量函数,以及当两个unique_ptrs指向同一个对象时程序的行为方式?
- 我是否可以使用 win32 句柄以编程方式记录发送到/接收到 USB/COM 的内容
- 我们是否应该转向新的自我分配保护方式?
- 以这种方式初始化的 char 数组是否会自动添加空终止符?
- clang++ 是否以更轻松的方式处理系统标头?
- size_t的大小和对齐方式是否与ptrdiff_t相同?
- 是否可以以编程方式构造 std::initializer_list?
- 是否有任何优雅的方式来遍历元素位置可以更改的列表?
- 只需要知道我在c ++中打印模式的方式是否有效,或者有另一种方法可以有效地做到这一点
- 是否可以在C++中以间接方式包含头文件
- C++:这两种将数字写入矩阵的方式之间是否存在显着的速度差异?
- 我的随机生成器是否不工作,或者我决定人/骨架是否击中对手的方式是否有错误
- 我是否以错误的方式声明了getpriorityvalues函数
- 是否有用于"go to variable type definition" C++中"auto"变量的 Visual Studio 键盘快捷方式?