0

I was following along a tutorial online about C#. The tutorial talks about passing-by-reference vs passing-by-value. What I am confused about specifically is that since in C#, there is a distinction between reference types vs value types, how does that distinction affect the output of the following two functions.

In the following code snippet:

  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;
    }

We pass in an object of type Book into SetName and we see that SetName correctly sets the name of book1 to "New Name", even though book is being passed by reference.

On the other hand in the code snippet below, this does not seem to be the case:

   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);
    }

Why is this the case?

Finally, in the code snippet below,

  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);
    }

we are passing in by reference. In this case, what is happening? How is this different from the first case?

user1234
  • 17
  • 5
  • @KenWhite I understand the distinction that the post makes between passing-by-value and passing-by-reference. But what I don't understand is that how is this logic being used in the examples above? It seems that both the first and the second functions do the same thing, yet the result is different. – user1234 Jul 31 '22 at 23:14
  • @KenWhite Additionally, the how pass-by-reference and pass-by-value behaves depends on what is being passed in. Because what I am passing in is in C# a reference type (and not a value type), I assume my question has more granularity than the one you pasted above which does not take into account value types vs reference types. – user1234 Jul 31 '22 at 23:16

2 Answers2

2

You've confused "pass by reference" with "reference type." Reference types are passed by value by default, unless you use the ref keyword. When you pass a reference type by value, it is the same as passing anything by value; you are passing a copy. In this case, you are passing a copy of the reference.

In this example...

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

...you are passing a reference type by value. The variable book is populated with a copy of the caller's variable book1. However, the variable itself contains a reference to the same Book object as the caller's, so setting its properties shows up in both places.

In this example...

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

...you are also passing a reference type by value. However, you are overwriting the (copied) value with a reference to a new Book. It's just a copy, and has no effect on book1.

In this example...

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

...you are passing a reference type by reference. The method receives a pointer to the original reference, which it can change. Therefore when you assign it a new Book, it shows up in both places.

John Wu
  • 50,556
  • 8
  • 44
  • 80
  • So in the last case, since book now holds a new reference to a new object, does book1 in the calling function also now contain a reference to this new object? – user1234 Jul 31 '22 at 23:22
  • Yes. When you pass by reference, the method doesn't receive a copy, it receives a reference to the original value (which is a reference to a Book object). Since it doesn't have its own copy, updating it affects the caller too. – John Wu Jul 31 '22 at 23:33
0

In your first case, book is not really being passed by reference but it IS a reference type. We know this because Book is a reference type. re: https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/reference-types and not a value type.

SO, when that first example private sets the book.Name = name; it does that in the book reference for the reference type it was passed.

In the second example, you set book to a NEW book of Book type which creates a NEW book for the reference and then sets the Name of that (not a good practice since it is doing TWO things really) - and of course the original reference does not know about the NEW book, only the book it has a reference to.

Now in the third example you pass a reference type by ref and then change it to a new Book with a new name - and since it is a reference, the original book1 picks up the new object and the name.

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

If it helps GetBookSetName should really be SetBookToNewBookWithNewName or some such for this last example.

Mark Schultheiss
  • 32,614
  • 12
  • 69
  • 100