在将值类型传递给 C# 中的方法时更改该值类型
本文关键字:类型 方法 | 更新日期: 2023-09-27 18:01:25
C# 的基本手册指出,要在传递给另一个方法时更改值类型,您必须使用 out
或 ref
关键字等。
例如:
int Loop(int counter)
{
return(++counter);
}
void ClickIt ()
{
int count = 0;
for (int c1 = 0; c1 < 10; c1++)
{
count = Loop(count);
Console.Writeline(count);
}
}
在这里,ClickIt
输出以下结果:1, 2, 3, 4, ... 10
在示例中,count
(值类型(正在从方法ClickIt
传递到方法Loop
而不out
或ref
,正在Loop
中更改。然后Loop
将count
返回到调用方法ClickIt
该方法将更改拾取count
。
所以,我的问题是:当值类型作为参数传递给另一个方法时,什么时候需要使用out
或ref
才能更改值?
你对"ref"的含义有不正确的理解,你也混淆了值和变量。 这些都是常见的错误。
让我们回到基础。
变量是可以包含值的存储位置。
让我们简化您的程序:
int M(int x)
{
x++;
return x;
}
void N()
{
int y = 0;
y = M(y);
}
如果调用 N,会发生什么?
假设一个变量是一个可以包含一张纸的抽屉。 我们做一个抽屉并贴上y标签。在y中,我们放了一张写着"0"的纸。 现在我们称之为 M(y(。会发生什么?
我们制作了一个标有"x"的新抽屉,并以">y"复印了这张纸。我们把副本放在抽屉里x.y包含一张纸,上面写着0,x包含另一张纸,也包含0。
现在在 M 中,我们递增 x。会发生什么? 我们制作一张写着 1 的新纸,扔掉旧纸,然后将新纸放在抽屉 x 中。
现在我们复印 x 中的值,所以我们有另一张纸,上面写着 1。 当 M 返回时,我们将那张纸放入 y 中,并扔掉已经存在的 0。
M 修改了 y 吗? 不,M 只修改了 x。 N 修改 y,两次。一次在创建 y 时,一次在 M 返回之后。
请注意,我们制作了两个副本。首先,我们在进入 M 的途中制作了 y 的副本并将其复制到 x,然后在离开的途中制作了 x 的副本并将其复制到 y。
现在假设我们有
void P(ref int b)
{
b++;
}
void Q()
{
int c = 0;
P(ref c);
}
我们运行 Q. 会发生什么?我们制作一个名为c的抽屉,并在里面放一张写着"0"的纸。 当我们调用P时会发生什么? 不同的东西。这次我们做了一个叫做b的抽屉,并在里面放了一张纸,上面写着"不要使用这个抽屉!每次尝试使用此抽屉时,请改用c! 也就是说,b 将其行为指向 c。
现在 P 尝试递增 b。它试图从 b 中获取一个值,但发现 b 说不,使用 c。所以它在c中查找,找到0,制作一张写着1的新纸,替换b的内容 - 不,等等,我们需要用1替换c的内容,然后返回。 所以 c 更新为 1。
为什么这两件事不同有意义吗? 第一种叫做复制输入,复制输出,因为我们在进入M的路上复印了y,在离开的路上复印了x。 第二个是通过引用调用的,因为 b 将其行为指向 c;不复制任何值。
在您的示例中,您实际上不会修改传递的计数变量。传递后,将在 Loop 函数作用域内创建副本。然后修改完成,你返回并设置为你的计数变量。
实际上,目的是:
- ref - 是变量在传递到函数之前应该已经初始化。并且副本不是在内部创建的。您可以直接修改传递的变量。因此 - 您不需要返回修改后的值并将其设置回您的变量。
- out - 它不需要在传递到函数之前初始化传递的变量。但它实际上必须在该函数中初始化。
希望仅此而已。
长评论...您的代码可能会令人困惑 - 确保将函数的结果与参数分开:
void ClickIt ()
{
int count =0;
for (int c1 =0; c1 < 10; c1++)
{
var resultCount = Loop(count);
Console.Writeline("Result:{0}, count:{1}", resultCount, count);
}
}
答案(基于意见( - 你几乎不应该使用 out
/ref
- 它比返回值更难推理。这些函数也很难在 LINQ/lambda 表达式中使用,因为需要参数是变量。
当函数返回多个结果(如TryParse
(时,它在某种程度上可以接受的常见情况,但请考虑其他返回类型(即可为空的int?
(是否也可以工作。