简体   繁体   中英

Why does collection initializer work with getter-only property?

It is very unpredictable result of code for me.

I did not expect this code to produce such a result.

So, I read Jeffrey Richter's book (clr ia c#) and there is a example with this code.

internal class ClassRoom
{
    private List<String> _students = new List<string>();
    public List<String> Students { get { return _students; } }
}

ClassRoom classRoom = new ClassRoom
{
    Students = {"Mike", "Johny", "Vlad", "Stas"}
};

foreach (var some in classRoom.Students)
{
    Console.WriteLine(some);
}

And, as some of you can suggest we can see in console four names. So, my question is:

How do we get our names, if they are going to get in Students and respectively in _students, that were not assigned because of nonexistence of Students setter.

__________EDIT_1_______________

I read your answers, and thank you for those.

But, you know, it is kind of misunderstanding... I really can't figure it out.

When I Initialize Students , I initialize _students ?

And what not less important, when I take variable from IEnumerable( some ), I am take variable from Students ? I think not.

I can't understand, WHERE exactly I assign variables(INITALIZE) _students collections ?

Thank you for help me to figure it out!

The _students field is assigned once internally at construction time, the lack of a public setter prevents it from being set to an different list instance from the outside. ClassRoom's object initializer is not actually setting the Students collection property but rather it is simply leveraging collection initializer syntax to add the initial items to Students ...

ClassRoom classRoom = new ClassRoom
{
    Students = { "Mike", "Johny", "Vlad", "Stas" }
};

The above is similar to the following though not exactly the same since the object initializer version does not perform the assignment to classRoom until after the collection is populated.

ClassRoom classRoom = new ClassRoom();
classRoom.Students.Add("Mike");
classRoom.Students.Add("Johny");
classRoom.Students.Add("Vlad");
classRoom.Students.Add("Stas");

Conversely, note that the following WILL produce a compiler error due to the lack of the public setter hence the access truly is restricted as expected...

ClassRoom classRoom = new ClassRoom
{
    Students = new List<string> { "Mike", "Johny", "Vlad", "Stas" }
}

These other answers are almost right. Let's go through it and actually get it all right.

Why does a collection initializer work with a getter-only property?

Because the property value is not changed. The contents of the collection that the property refers to is what is changed.

Think of a property as a bucket. You can't change which bucket the property refers to, but you can change the contents of the bucket.

How do we get our names, if they are going to get in Students and respectively in _students, that were not assigned because of nonexistence of Students setter.

The Students property is never assigned, at all. How could it be? It has no setter.

The names are added to the collection referred to by the Students getter.

When I Initialize Students, I initialize _students ?

No. The initialization of _students is the line which assigns a value to _students.

The collection initializer adds values to an existing collection . The variable _students has already been initialized.

when I take variable from IEnumerable(some), I am take variable from Students ?

You don't take a variable from anything. You take values . Variables are not values; variables contain values. Don't confuse the storage with the value stored.

I can't understand, WHERE exactly I assign variables(INITALIZE) _students collections ?

The _students variable is initialized in the line that initializes _students. The contents of the collection are initialized in the line that has the contents.

Perhaps it will help to list everything that happens in exactly the order it happens:

  • a new Classroom object is created; the _students field is null; a reference to the object is produced.
  • a new empty list is created; a reference to the list is copied into the _students field of the newly-created Classroom object.
  • the Students getter is called on the reference to the new Classroom object. It returns the contents of _students, which is a reference to an empty list.
  • four items are added to that list
  • the reference to the Classroom object is copied to the classroom variable.

Or, in code, this is equivalent to:

c = create a Classroom
c._students = null
l1 = create a List<string>
c._students = l1
l2 = c.Students -- which returns c._students
l2.Add("...")  -- four times
classRoom = c

See, there is never a setter of Students called. There doesn't need to be. All it does is gets the reference contained in _students, which already has been initialized by the field initializer.

By saying Students = {"Mike", "Johny", "Vlad", "Stas"} what it's doing is

property Students returns the underlying List<String> _students instance and on that you are adding the string literal using a collection initializer.

So, when I iterate through Students, am I iterate through the _students ?

Technically YES, your _students is a private variable and Students property is wrapper around that private field to give access to that list to outside consumer.

So when you say Students = ... you are actually setting or initializing the private field since accessing Students gives you back the List<string> instance.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM