【问题标题】:C# reference type questionsC# 引用类型问题
【发布时间】:2012-12-06 17:41:18
【问题描述】:
 public class TreeNode
{
    public TreeNode Left;
    public TreeNode Right;
    public int Data { get; set; }

    public TreeNode(int data)
    {
        Left = null;
        Right = null;
        Data = data;
    }
}



   public class BinarySearchTree
{
    private TreeNode _root;

    public BinarySearchTree()
    {
        _root = null;
    }

    public void Insert(int data)
    {
        TreeNode tempNode = null;

        Insert(_root, tempNode);
    } 

    private void Insert(TreeNode treeNode, TreeNode newNode)
    {
        newNode = new TreeNode(3);
        treeNode = new TreeNode(4);
   }

在这个例子中,public void Insert(int data) 方法基本上只是调用私有方法,我在public void Insert(int data) 方法的末尾设置了一个断点。

注意到_root 仍然为空,而tempNode 为4。我不明白为什么?谁能解释一下?

【问题讨论】:

  • 代码可以编译吗?在 Insert(int) 中 newNode 定义在哪里?
  • 您正在按值传递引用。如果您执行了 treeNode.DoWork(),它将作用于被引用的对象,但如果您重新分配 treeNode,则您正在重新分配引用本身(通过值传递)。
  • 框架设计指南(Krzysztof Cwalina 和 Brad Abrams 合着的一本书)建议避免使用 ref 和 out 参数。很少有充分的理由使用outref
  • 我的问题是为什么 _root 仍然为空,而 tempNode 为 4。
  • 请考虑以使预期行为与名称相关的方式命名方法。现在“插入”更像是“CreateSomeNodes”...

标签: c# pass-by-reference


【解决方案1】:

通过引用传递类使用ref关键字:

private void Insert(ref TreeNode treeNode, ref TreeNode newNode)
{
    newNode = new TreeNode(3);
    treeNode = new TreeNode(4);
}

在您的原始方法中,仅传递对类的引用的副本,而不是通过引用传递。因此,如果您正在执行new,您只是在替换类引用的本地副本,但调用方法仍保留原始引用值。因此,新创建的值不会返回给调用者。

改变你的Insert(data)方法(没有ref关键字编译器会给你一个错误):

public void Insert(int data)
{
    TreeNode tempNode = null;
    Insert(ref _root, ref tempNode);
} 

这实际上是编译器的要求,只是为了确保调用者知道在方法调用之后传递的对象可能会被其他对象替换。

您可能还想了解out 关键字。 ref 参数应该在调用之前初始化,out 参数应该在方法内部进行初始化。

【讨论】:

    【解决方案2】:

    您传递的是引用类型,但它实际上传递了引用的副本。更新该副本不会更新原始副本。

    要解决这个问题,要么让方法返回新值。或者您可以重新编写函数以使用 out 参数。

    private void Insert(out TreeNode treeNode, out TreeNode newNode)
    {
        newNode = new TreeNode(3);
        treeNode = new TreeNode(4);
    }
    

    【讨论】:

    • 这里使用ref应该没有什么问题,因为他在创建一个新对象,只要他在函数和函数调用中都使用ref关键字。
    • 对所有参考/输出建议 +1。不幸的是,该方法的原始名称很难理解预期的行为是什么......
    【解决方案3】:

    查看参数的outref 修饰符。例如,你可以

    private void Insert(ref TreeNode treeNode, TreeNode newNode)
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2020-11-07
      • 2017-07-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-11-11
      • 1970-01-01
      相关资源
      最近更新 更多