【问题标题】:Changing a value type when it is passed to a method in C#将值类型传递给 C# 中的方法时更改值类型
【发布时间】:2014-05-03 18:28:17
【问题描述】:

关于 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 以便可以更改值?

【问题讨论】:

  • 对象通过 ref 传递,对于 struct 和基本数据类型,它是通过 val 除非指定了 out 或 ref
  • 这些更改通过Loop的参数传回。更改将写入您的 count 变量,因为您明确分配了 Loop 的返回值。

标签: c# methods parameter-passing value-type


【解决方案1】:

您对“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 的内容——不,等等,我们需要将 c 的内容替换为 1,然后返回。所以 c 更新为 1。

这两件事为什么不同有意义吗?第一个称为copy in, copy out,因为我们在进入 M 的过程中制作了 y 的复印件,并在离开时制作了 x 的副本。第二个被称为 by reference 因为 b 将其行为指向 c;没有值被复制。

【讨论】:

    【解决方案2】:

    在您的示例中,您实际上并没有修改传递的计数变量。 当它被传递时,将在 Loop 函数范围内创建一个副本。然后修改完成,您返回并设置为您的计数变量。

    其实目的:

    1. ref - 变量在传递给函数之前应该已经初始化。并且不会在内部创建副本。您直接修改传递的变量。结果 - 您无需返回修改后的值并将其设置回您的变量。
    2. out - 它不需要在传递给你的函数之前初始化传递的变量。但它实际上必须在该函数内部进行初始化。

    希望就是这样。

    【讨论】:

      【解决方案3】:

      长评论... 您的代码可能会令人困惑 - 确保将函数的结果与参数分开:

      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?)是否也可以工作。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2015-02-03
        • 2013-08-13
        • 1970-01-01
        • 1970-01-01
        • 2019-10-27
        • 2012-12-26
        • 1970-01-01
        相关资源
        最近更新 更多