【问题标题】:Value type and reference type problem值类型和引用类型问题
【发布时间】:2011-05-20 10:56:43
【问题描述】:

嗨 我正在尝试对两个对象进行简单交换。我的代码是

void Main()
{
  object First = 5;
  object Second = 10;

  Swap(First, Second);
  //If I display results it displays as 
  //Value of First as 5 and Second as 10
}

private static void Swap(object First, object Second)
{
  object temp = First;
  First = Second;
  Second = temp;
}

由于对象是引用类型,它的引用应该被传递给方法并且它应该交换。 为什么没有发生?

【问题讨论】:

标签: c# .net swap reference-type


【解决方案1】:

这样看。

让我们将交换方法中的参数重命名为“x”和“y”,因为将它们与本地参数相同只会令人困惑。

你有一个苹果和一个橙子。两个对象。

您有两张纸,上面写着“APPLE”和“ORANGE”。这些是对对象的引用。

您有两个框,一个标为 First,一个标为 Second。

你把“APPLE”放在第一位,把“ORANGE”放在第二位。

现在您又获得了两个标记为 x 和 y 的框。

您复印一份 APPLE 并将其放入 x 中。你把 ORANGE 复印一份放在 y 里。

现在你交换 x 和 y 的内容。 APPLE 和 ORANGE 仍然是引用,但您没有更改 First 和 Second 的内容。你改变了 x 和 y 的内容。

现在假设您将“ref”添加到参数 x 和 y 的声明中。这意味着:

您有两个框,一个标为 First,一个标为 Second。

你把“APPLE”放在第一位,把“ORANGE”放在第二位。

你先给 box 添加一个新标签;它现在也可以称为 x。 Second 也可以称为 y。

你交换 x 和 y 的内容。由于它们只是 First 和 Second 的别名,因此 First 和 Second 的内容交换了。

有意义吗?

【讨论】:

    【解决方案2】:

    这里有各种不同的东西:

    • 对象在堆中存活。暂时忘记它们
    • 对对象的引用存储在 First 和第二个中
    • Main 中有两个变量
    • Swap 中有两个 参数

    现在;重要的是“引用类型/引用”和“按引用传递”之间的区别。它们完全不相关。

    在行中:

    Swap(First, Second);
    

    您将两个变量的值传递给Swap。在这种情况下,First / Second 的值是对装箱对象的引用。

    下一个;

    private static void Swap(object First, object Second)
    {
      object temp = First;
      First = Second;
      Second = temp;
    }
    

    在这里,您交换两个本地参数的值,但它们完全独立于其他任何东西。如果我们想让调用者看到值的变化(即重新赋值),我们需要通过引用传递:

    private static void Swap(ref object First, ref object Second)
    {
      object temp = First;
      First = Second;
      Second = temp;
    }
    

    现在First 的值不再是对装箱对象的引用;它是对装箱对象的引用。在调用方,我们使用:

    Swap(ref First,ref Second);
    

    表示传递变量First的引用,而不是传递变量First的值。

    请注意,我说过您可以忘记存在对象的事实?如果我们使用上述所有内容,则完全相同:

    int x = 1, y = 2;
    Swap(ref x, ref y);
    void Swap(ref int a, ref int b) {
        var tmp = a; a = b; b = tmp;
    }
    

    唯一的区别是x的值是1等,而ref x是对变量x的引用。在按引用传递中,引用类型与值类型完全无关;唯一重要的是理解您默认传递变量的 value,其中变量的 value 是 1 (等),或“对一个东西”。无论哪种方式,逻辑都是一样的。

    【讨论】:

    • 如果我必须作为 ref obj 传递值类型和引用类型有什么区别
    • @Chikaka - 这听起来可能是循环的,但“值类型语义与引用类型语义”。我的意思是:标准 变量*/*参数是否包含a:值本身(值类型)或b:对对象的引用(引用类型)。这样做的副作用是:多个变量可以引用 same 引用类型,其中与值类型一样,每次分配时都会复制结构。在您的情况下,您有一个盒装对象,它是引用类型。但这里的关键是您的 Swap 重新分配参数。要观察这一点,您需要通过引用传递。
    • 类 GraphPoint { public int X { get;放; } 公共 int Y { 获取;放; } } void button1_Click(object sender, EventArgs e) { GraphPoint First = new GraphPoint() { X = 1, Y = 1 }; GraphPoint Second = new GraphPoint() { X = 2, Y = 2 }; Mofiy(第一); //如果我显示结果,它会显示 //First 的值为 5 } void Mofiy(GraphPoint First) { First.X = 5;首先.Y = 5;如果不传递引用,调用 modify 方法后 Main 中 First 的值如何变化。
    • @Chilaka - 注意我两次使用了 reassign 这个词?关键是在Swap 你给参数不同的值 - First = ...。在您刚刚给出的示例中,您只需更改对象的属性。请注意,发生变化的地方现在完全不同了——reference(参数的值)没有改变,但是堆上的远处对象发生了改变。如果您只是更改对象,调用者会自动看到 - 毕竟,它是同一个对象。
    • 感谢您帮助我更好地理解它。
    【解决方案3】:

    Swap 方法内的参数所指向的对象是对Main 内的First 和Second 对象的引用,但参数本身是Swap 方法的本地对象。

    所以如果你在Swap 里面写了First = 1; Second = 2;,你就会看到Main 里面的对象的变化。但是,您只需将参数更改为Swap 指向(通过将它们分配给另一个对象),而根本不更改对象。如果您尝试在 Swap 方法中将对象设置为 null,情况也是如此。

    【讨论】:

      【解决方案4】:

      您没有将其作为参考传递,您需要明确声明您正在传递参考:

       Swap(ref First,ref Second);
      
       private static void Swap(ref object First,ref object Second)
      

      【讨论】:

      • 如果我必须通过 ref 关键字显式传递。那么 wat 是 vale 类型和引用类型的区别
      • 因为它在另一种方法中,所以您必须明确表示要传递引用,就好像它在同一种方法中一样。大多数时候,虽然您不需要传递引用,而只想传递对象的值,这就是为什么您必须将其指定为引用
      • 试试这个类点{public int X;} 现在将类点的对象传递给其他方法(比如修改)并在这个函数中改变它的X值。在修改方法之后的主方法中,检查它会改变的 X 的值(不要通过 ref 传递)。这是怎么回事?
      • 我认为这是深拷贝与浅拷贝的情况,请看这里:stackoverflow.com/questions/184710/… 要添加到您的修改方法,请将点类分配给新的点类,然后更改X 的值,你现在会看到它并没有改变初始类的值
      • 我建议您向@Mark Gravell 提出这个问题,以便他确认这一点
      【解决方案5】:

      您正在传递对对象的引用按值(按值传递是 C# 中的默认值)。

      相反,您需要将对象传递给函数通过引用(使用ref 关键字)。

      private static void Swap(ref object first, ref object second)
      {
          object temp = first;
          first = second;
          second = temp;
      }
      
      void Main()
      {
          object first = 5;
          object second = 10;
      
          Swap(ref first, ref second);
      }
      

      当然,如果您使用的是 C# 2.0 或更高版本,最好定义此函数的 generic 版本,它可以接受任何类型的参数,而不是 object 类型。例如:

      private static void Swap<T>(ref T first, ref T second)
      {
          T temp;
          temp = first;
          first = second;
          second = temp;
      }
      

      然后你也可以用它们的正确类型声明变量,int。

      【讨论】:

        【解决方案6】:

        您需要通过引用传递对象,而不是通过值传递对对象的引用。换句话说,您需要传递对对象的引用的引用。

        这是使用ref 关键字实现的。

        例如

        private static void Swap(ref object First, ref object Second)
        {
          object temp = First;
          First = Second;
          Second = temp;
        }
        

        【讨论】:

        • 这里的措辞有点偏离,IMO。你永远不会传递对象;当你做ref object时,你通过references, by reference
        • @Marc:抱歉,我会改写的!
        【解决方案7】:

        你需要通过引用传递。

        这里有一些关于 pass-by-refence、pass-by-value 的额外信息: http://www.yoda.arachsys.com/csharp/parameters.html, 试试这个:

        void Main()
        {
         object First = 5;
         object Second = 10;
        
          Swap(ref First, ref Second);
          //If I display results it displays as 
          //Value of First as 5 and Second as 10
        }
        
        
        private static void Swap(ref object First, ref object Second)
        {
          object temp = First;
          First = Second;
          Second = temp;
        }
        

        【讨论】:

        • 您还需要将 ref 添加到方法声明中
        猜你喜欢
        • 2012-07-20
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-02-16
        • 2014-09-21
        • 2015-04-29
        • 2015-07-18
        • 2013-06-01
        相关资源
        最近更新 更多