【问题标题】:'ref' not working like I think it should'ref' 不像我认为的那样工作
【发布时间】:2011-09-29 02:16:36
【问题描述】:

我有以下代码,当它运行时查看它显示初始的“myInt”和“myFloat”在方法调用返回之前不会改变它们的值。每次在被调用的方法中更改它们的值时,它们的值不应该改变吗,因为它们每次都作为“ref”传递?

class Tester
{
    public void Run()
    {
        int myInt = 42;
        float myFloat = 9.685f;
        Console.WriteLine("Before starting: \n value of myInt: {0} \n value of myFloat: {1}", myInt, myFloat);
        // pass the variables by reference
        Multiply( ref myInt, ref myFloat );
        Console.WriteLine("After finishing: \n value of myInt: {0} \n value of myFloat: {1}", myInt, myFloat);
     }
     private static void Multiply (ref int theInt, ref float theFloat)
     {
        theInt = theInt * 2;
        theFloat = theFloat *2;
        Divide( ref theInt, ref theFloat);
     }
     private static void Divide (ref int theInt, ref float theFloat)
     {
        theInt = theInt / 3;
        theFloat = theFloat / 3;
        Add(ref theInt, ref theFloat);
     }
     public static void Add(ref int theInt, ref float theFloat)
     {
        theInt = theInt + theInt;
        theFloat = theFloat + theFloat;
     }
     static void Main()
     {
        Tester t = new Tester();
        t.Run();
     }
}

【问题讨论】:

  • 您是否在调试器中查看它们的值?你在观察什么价值观?具体调用哪个方法?
  • 我似乎找不到相关的 SO 问题,但我很确定这种情况(通过 ref 传递值,其中局部范围变量与 ref 参数具有相同的名称)混淆了调试器,它可能不会显示正确的值。它对代码执行没有影响,只是在调试时实时检查。
  • 并且仅在图形调试器上 - 命令行应返回正确的 this.myInt 值。
  • @sixlettervariables 是的,我正在调试器中将它们添加到我的监视列表中。一旦它进入第一个被调用的方法,它们就会变得模糊/变灰,但它们的值(以及它们的变灰/模糊状态)在调用堆栈返回到 Run() 方法之前不会改变。
  • 此外,如果您正在备份调用堆栈以从命令行查看值,它也不会是最新的,因为您在执行此操作时已经及时返回。作为@Fernaref,这只会影响在当前断点处使用 GUI 在活动过程之外进行检查。

标签: c# .net pass-by-reference value-type


【解决方案1】:

编辑:好的,已经在您的 cmets 中看到了描述...

如果您在(例如)Add 中放置断点,那么您的监视变量将不会改变,除非您让它们重新评估 - 这必须在正确的堆栈中完成。当断点被命中时,进入调用堆栈视图,双击“运行”方法(它不会改变你要去的地方,只是你正在查看的堆栈帧),你会看到值更新。

【讨论】:

  • 我认为他的意思是 myIntmyFloat 在主要范围内,在 Run 方法中 - 而不是其他方法中的参数变量。 IE。使用调试器检查这些变量,它们在Multiply 调用完成之前不会改变(OP:“initial 'myInt' 和 'myFloat' 在方法调用返回之前不会改变它们的值回来。”)
  • @Kieren:他们应该这样做。如果您在Add 中放置一个断点,您应该看到值已更改。如果不这样做,那只是调试器异常。
  • 我认为答案是,'这是一个调试器异常'然后:) 我会去 -
  • @Kieren:是的,感谢您指出问题的含义。我已经编辑了我的答案,并将在 VS 2010 中尝试。
  • 我会说 myInt 和 myFloat 超出范围,因此实际上没有值。当然,它们在其他范围内指向的内存位置在多个范围内有一个值,甚至可能有几个值,但在乘法范围内它们不存在
【解决方案2】:

这是调试器异常,根据 Jon 和 jamietre 的 cmets,值会立即更改。

【讨论】:

  • 可能是这样,但奇怪的是,我正在使用的这本书,关于这个例子/练习有以下问题:“停止调试,再次运行程序,当它到达 Run() 中的断点,在 'myInt' 上设置一个监视。逐步执行这些方法。'myInt' 的值什么时候改变?然后它在附录中回答了这个问题:“在 Multiply() 方法完成后,控制权返回到 Run() 之前,myInt 的值不会改变。
  • 这可能意味着不同的myIntmyInt 是其他调用方法中的参数,而不是主要方法!
  • 无论我“观察”参数还是“主要参数”都一样
  • ...对我来说听起来像是调试器异常!
  • @Gijera:这是哪本书?这是不正确的 - myInt 的值真的确实立即改变。我会非常怀疑这本书,它似乎使用调试器异常作为不正确结论的理由。
【解决方案3】:

您在表达式 myIntmyFloat 而不是表达式 theInttheFloat 上放置了 Watch,因此您不再看到它们的值。 当前范围中不存在myIntmyFloat

您可以在调用堆栈中备份以观察它们的值,或监视您关心的所有四个表达式

【讨论】:

  • 我试过刷新,我得到的只是“myInt The name 'myInt' does not exist in the current context”。另外,为什么范围很重要?他们不应该在堆上分配(尽管我可能不完全理解堆,所以是的......)因此可以从任何地方访问?最后,“要求重新评估”是什么意思?
  • 我更新了我的图片以反映实际情况。我本来打算单击刷新以显示问题,但我意识到这没有帮助:) 范围很重要,因为监视窗口正在查看 表达式表达式 myIntAdd()Multiply() 中不存在,但theInt 确实存在!您遇到了一个问题,您认为您已经要求调试器在特定范围内观察特定变量,而是要求在每个步骤中评估给定的表达式。
  • 突出显示 theInt * 2 之类的内容,右键单击并转到 添加手表。当您逐步了解每种方法时,您会更好地理解 Watch Window 提供的功能。
  • 有没有可以做我想做的调试工具?与其查看表达式,不如仅查看内存中对象的值?
  • 是和不是。是的,堆在任何地方都可用。不,myIntmyFloat 不在“堆”上,t 是。不,这并不意味着您可以只观看表达式t 并在任何地方看到它。您不应该非常关心大多数 .Net 应用程序中的堆与堆栈分配。 .Net 内存模型的完整解释比这个注释框所能处理的要多一点。搜索"stack heap .net" 了解一些问题。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多