在将值类型传递给 C# 中的方法时更改该值类型

本文关键字:类型 方法 | 更新日期: 2023-09-27 18:01:25

关于

C# 的基本手册指出,要在传递给另一个方法时更改值类型,您必须使用 outref 关键字等。

例如:

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而不outref,正在Loop中更改。然后Loopcount返回到调用方法ClickIt该方法将更改拾取count

所以,我的问题是:当值类型作为参数传递给另一个方法时,什么时候需要使用outref才能更改值?

在将值类型传递给 C# 中的方法时更改该值类型

你对"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 函数作用域内创建副本。然后修改完成,你返回并设置为你的计数变量。

实际上,目的是:

  1. ref - 是变量在传递到函数之前应该已经初始化。并且副本不是在内部创建的。您可以直接修改传递的变量。因此 - 您不需要返回修改后的值并将其设置回您的变量。
  2. 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?(是否也可以工作。