3

Why is the Tags property of Book empty after this code runs?

class Program
{
    static void Main(string[] args)
    {
        List<Book> books = new List<Book>();
        List<String> tags = new List<String> {"tag1", "tag2", "tag3"};
        String title = "a title";
        books.Add(new Book
        {
            Title = title,
            Author = "an author",
            Tags = tags
        });
        Console.WriteLine("(" + title + ")");
        Console.WriteLine((books[0]).Tags.Count());

        title = String.Empty;
        tags.Clear();

        Console.WriteLine("(" + title + ")");
        Console.WriteLine((books[0]).Tags.Count());
    }
}

The code for Book:

public class Book
{
    public String Title { get; set; }
    public String Author { get; set; }
    public List<String> Tags { get; set; }
}

Running this code outputs

("a title")
3
()
0

Are tags and title being passed by reference here? Renaming the respective properties produces the same output.

EDIT:

I just realised that I meant for every Console.WriteLine statement to refer to the object, not just the tags one. I meant this:

Book aBook = books[0];
Console.WriteLine("(" + aBook.Title + ")");
Console.WriteLine(aBook.Tags.Count());

title = String.Empty;
tags.Clear();

Console.WriteLine("(" + aBook.Title + ")");
Console.WriteLine(aBook.Tags.Count());

which as expected, outputs:

("a title")
3
("a title")
0

but since I made a mistake in my initial question, I'm leaving it as is since the parts of the answers that refer to title are referencing the original code.

Ricardo Altamirano
  • 14,650
  • 21
  • 72
  • 105

3 Answers3

5

List<T> is a reference type, so yes, you get reference semantics here.

You need to assign a copy of tags to the property if you want them to be independent.

Tags = new List<string>(tags);
Oded
  • 489,969
  • 99
  • 883
  • 1,009
  • +1 Is `String` also a reference type? I noticed this behaviour occurs with `title` too? (Sorry about editing without noticing the answer). – Ricardo Altamirano Jul 12 '12 at 16:28
  • 1
    @RicardoAltamirano - It is (all _classes_ are reference types, all structures are value types). – Oded Jul 12 '12 at 16:29
  • Ah, ok. That makes more sense. – Ricardo Altamirano Jul 12 '12 at 16:30
  • `string`s are immutable, (meaning, e.g. that `title.Replace('j', 'q')` doesn't change `title`, but creates a new string with the value) so they behave more like values than references in this instance. E.g. doing `title = null` after creating the `Book` won't change the book's title, only your local variable `title`. If you look at `books[0].Title` instead of `title` you'll see that it is unchanged. – Tim S. Jul 12 '12 at 16:32
  • 2
    @TimS. Mutability and reference semantics are quite different, and independant. The fact that `string` is immutable just makes it harder to observe the fact that you are dealing with references as the easiest way to observe the fact that two variables reference the same object is to mutate that object. Just because an object is immutable doesn't make it 'behave like a value'. It behaves exactly like a reference, because it is one. Blurring the line between reference semantics and mutability just makes understanding each that much harder, not easier. – Servy Jul 12 '12 at 16:59
1

The Tags property of your Book object refers to the same object instance as the list you created on your second line of code (List<String> tags = new List<String> {"tag1", "tag2", "tag3"};).

When you wrote Tags = tags, you're actually saying that Tags points to the same instance that tags.

That's why when you clear tags, Tags is cleared too as it's the same object reference.

ken2k
  • 48,145
  • 10
  • 116
  • 176
1

Are tags and title being passed by reference here?

Yes.

However, the confusion (based on comments) appears to be due to the difference in how Tags and Title behave in your code.

The difference in behavior between Tags and Title when you do:

title = String.Empty;
tags.Clear();

Is due to the fact that, in the first case, you're assigning a completely new instance to title. When you call tags.Clear(), however, you're mutating the existing instance, which is "shared" with the Tags property within book[0] (since List<string> is a class and has reference type semantics).

Reed Copsey
  • 554,122
  • 78
  • 1,158
  • 1,373
  • I'm stupid. I meant to edit the question to use `Book.Title` in the `Console.WriteLine` statement. If I make that change (changing `title` to `(books[0]).Title` in the print statement) the behaviour is as I expected (see my edit). I left the original question as is, though. – Ricardo Altamirano Jul 12 '12 at 16:33