C# 中按值传递与按引用传递的含义 [重复]

Implications of pass-by-value vs pass-by-reference in C# [duplicate]

提问人:user1234 提问时间:8/1/2022 最后编辑:user1234 更新时间:8/1/2022 访问量:56

问:

我正在关注有关 C# 的在线教程。本教程讨论按引用传递与按值传递。我特别感到困惑的是,由于在 C# 中,引用类型与值类型之间存在区别,那么这种区别如何影响以下两个函数的输出。

在以下代码片段中:

  public void CanSetNameFromReference()
    {
        Book book1 = GetBook("Book 1");
        SetName(book1, "New Name");
        Assert.Equal("New Name", book1.Name);

    }

    private void SetName(Book book, string name)
    {
        book.Name = name;
    }

我们将 Book 类型的对象传递到 SetName 中,我们看到 SetName 正确地将 book1 的名称设置为“New Name”,即使 book 是通过引用传递的。

另一方面,在下面的代码片段中,情况似乎并非如此:

   public void CSharpIsPassByValue()
    {
        var book1 = GetBook("Book 1");
        GetBookSetName(book1, "New Name");

        Assert.Equal("Book 1", book1.Name);
    }

    private void GetBookSetName(Book book, string name)
    {
        book = new Book(name);
    }

为什么会这样?

最后,在下面的代码片段中,

  public void CSharpCanPassByReference()
    {
        var book1 = GetBook("Book 1");
        GetBookSetName(ref book1, "New Name");
        
        Assert.Equal("New Name", book1.Name);
    }

    private void GetBookSetName(ref Book book, string name)
    {
        book = new Book(name);
    }

我们通过引用传递。在这种情况下,发生了什么?这与第一种情况有何不同?

C# OOP 参数传递 引用

评论

0赞 user1234 8/1/2022
@KenWhite 我理解该帖子在传递值和传递引用之间的区别。但我不明白的是,在上面的例子中如何使用这个逻辑?似乎第一个函数和第二个函数都做同样的事情,但结果是不同的。
0赞 user1234 8/1/2022
@KenWhite 此外,按引用传递和按值传递的行为方式取决于传入的内容。因为我传入的内容在 C# 中是引用类型(而不是值类型),所以我认为我的问题比您上面粘贴的问题更细化,它没有考虑值类型与引用类型。

答:

2赞 John Wu 8/1/2022 #1

您混淆了“通过引用传递”和“引用类型”。默认情况下,引用类型按值传递,除非使用关键字。按值传递引用类型时,它与按值传递任何内容相同;您正在传递副本。在本例中,您将传递引用的副本。ref

在此示例中...

private void SetName(Book book, string name)
{
    book.Name = name;
}

...您正在按值传递引用类型。该变量填充了调用方变量的副本。但是,变量本身包含对调用方对象的同一对象的引用,因此设置其属性会显示在两个位置。bookbook1Book

在此示例中...

private void GetBookSetName(Book book, string name)
{
    book = new Book(name);
}

...您还按值传递引用类型。但是,您正在用对 .它只是一个副本,对 .Bookbook1

在此示例中...

private void GetBookSetName(ref Book book, string name)
{
    book = new Book(name);
}

...您正在逐个引用传递引用类型。该方法接收指向原始引用的指针,它可以更改该指针。因此,当您为其分配 新 时,它会显示在两个位置。Book

评论

0赞 user1234 8/1/2022
因此,在最后一种情况下,由于 book 现在包含对新对象的新引用,那么调用函数中的 book1 现在是否也包含对这个新对象的引用?
0赞 John Wu 8/1/2022
是的。通过引用传递时,该方法不会接收副本,而是接收对原始值的引用(这是对 Book 对象的引用)。由于它没有自己的副本,因此更新它也会影响调用方。
0赞 Mark Schultheiss 8/1/2022 #2

在第一种情况下,book 并不是真正通过引用传递的,而是一种引用类型。我们知道这一点,因为这是一个引用类型。回复:https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/reference-types,而不是值类型。Book

因此,当第一个示例私有设置时,它会在它传递的引用类型的引用中执行该操作。book.Name = name;book

在第二个示例中,您设置为一个 NEW 类型,该类型为引用创建一个 NEW book,然后设置该 of (这不是一个好的做法,因为它实际上在做两件事)——当然,原始引用不知道 NEW book,只知道它引用的书。bookbookBookName

现在,在第三个示例中,您传递一个引用类型,然后将其更改为具有新名称的新引用 - 由于它是引用,因此原始引用将选取新对象和名称。refBookbook1

private void GetBookSetName(ref Book book, string name)
{
    book = new Book(name);
}

如果它有帮助,对于最后一个例子来说,真的应该是这样。GetBookSetNameSetBookToNewBookWithNewName

评论

0赞 Mark Schultheiss 8/1/2022
在当天晚些时候,我希望这有助于以某种方式澄清这个概念。