【问题标题】:C# Object manipulation in method方法中的 C# 对象操作
【发布时间】:2022-01-08 21:44:11
【问题描述】:

我无法理解对象操作在 C# 方法中的工作原理。我知道,当您将对象传递给方法时,会传递对该对象的引用。考虑到这一点,我认为选项 1 会像这样工作:

  1. root 变量作为对 root 的引用进入方法
  2. 当您将其设置为 root.left 时,它会将该引用更新到 root 的左侧
  3. 然后,当您将其设置为新节点时,root 现在将在其左侧字段中有一个成员。

但是,只有选项 2 有效。这是为什么呢?

如果可以,请忽略它是静态的,这只是我为了解其工作原理而制作的一个示例。我尝试在不使用任何成员变量的情况下使其不是静态的,它做了同样的事情。

class binaryTreeNode 
{
    public binaryTreeNode left;
    public binaryTreeNode right;
    public int data;
    private binaryTreeNode(int data)
    {
        this.data = data;
    }
    public binaryTreeNode() {}


    public static void insert(binaryTreeNode root, int data) 
    {
        binaryTreeNode newNode = new binaryTreeNode(data);
        
        //option 1
        root = root.left;
        root = newNode;

        //option 2
        root.left = newNode;

        // in main():
        // binaryTreeNode root = new binaryTreeNode();
        // root.data = 5;
        // binaryTreeNode.insert(root, 3);
        // Console.WriteLine(root.left.data);
        // null reference on option 1
        // outputs properly (int data) on option 2
    }
}

【问题讨论】:

  • 在 C# 中,类和方法名应该是 Pascal-case,而不是 camel-case。
  • 我在答案中添加了一个示例。您可以检查一下,如果它有意义,您应该对参考有信心。

标签: c# object reference


【解决方案1】:

你的第三步错了。每当您使用= 时,您只需将右侧引用分配给符号左侧的任何变量。考虑到这一点,您只需将root 的引用从root.left 更改为newNode,但root.left 将保持不变。因此,上一行是无用的,选项 2 是要走的路。

让我们看一些例子,让你的思维方式与参考正确。

首先,让我们创建一个非常简单的类:

public class Model
{
    public int Value;

    public Model(int value)
    {
        this.Value = value;
    }
}

现在让我们看一些例子:

示例 1:

var model = new Model(3);

创建一个值为3Model 实例并将其分配给名为model 的变量。

var reference = model;

将保存在model 中的实例分配给reference。它们现在都指向同一个实例。

reference.Value = 2;

将实例的Value 属性更改为2

Console.WriteLine(model.Value);

输出 2,因为变量 referencemodel 指向同一个实例。

示例 2

var model = new Model(3);

创建一个值为3Model 实例,并将其分配给名为model 的变量。

var model2 = new Model(4);

创建另一个值为4Model 实例,并将其分配给名为model2 的变量。

var reference = model;

model 中保存的第一个实例分配给reference。它们现在都指向同一个实例。

reference = model2;

将保存在model2 中的第二个实例分配给reference。因此变量 modelreference 现在不再指向同一个实例。

reference.Value = 2;

将第二个实例的Value 属性更改为2。因此,第一个实例的 Value 属性值保持不变并继续为 3

Console.WriteLine(model.Value);

输出3

Console.WriteLine(model2.Value);

输出2

【讨论】:

  • 感谢您提供清晰明确的答案!因此,如果我理解正确,则传递给方法的任何对象本质上都是作为该类型的指针传递的。使用赋值= 只会改变指针指向的位置。在 C++ 中,您可以使用 * 运算符取消引用指针,然后您可以实际更改所指向的值。有什么方法可以在 C# 中做到这一点和/或我的思维过程是否错误?
  • “使用赋值 = 只是改变指针指向的位置”:完全正确。
  • 您也可以在 C# 中使用指针工作,但不建议这样做,您必须在“不安全”块中这样做。我以前从未这样做过,所以在那里我帮不了你太多,但应该有足够的信息来说明如何做到这一点。但是,通常您不应该这样做。如果要更改变量指向的位置,只需将其重新分配给指向相关实例的引用即可。我的例子应该可以帮助你理解这一点。
  • 要记住的另一件非常重要的事情是,只有类实例才是引用。如果通过 = 将值类型(如 int、bool 或任何结构)分配给变量,则右侧的值将传递给变量,但它们都代表自己的值,并且不引用相同的值。如果您复制我发布的任何一个示例并将 Model 类更改为结构,您会注意到输出不会发生变化,因为变量确实指向不同的(新复制的)实例。
  • 非常感谢您的参考和所有示例!像你这样的人帮了很多忙!
【解决方案2】:

简单来说,选项一 - 将引用本身视为一个单独的指针对象 - 此对象指向您传递给方法的实际实例。
因此,在您的方法中,您将一个对象“root.left”分配给该引用“object”,因此该引用对象现在指向“root.left”实例。 再一次,另一个分配 - 再次,根引用指向“newNode”实例。但实际上,在方法内部传递引用的原始根对象并没有被修改。
只有引用已被修改,它现在指向别的东西。

选项 2 实际上更改了对象内部的某些内容,传递的引用指向该对象 - 因此修改了原始的实际根对象。

这有点简单,但至少应该为您指明正确的方向。

【讨论】: