UPDATE #2 WHY A PRIVATE SETTER IS USELESS
OP commented asked why not use a private setter on a list? The answer is because while someone cannot change the reference to the overall list, they can still change individual items.
Example code:
A very simple User class
public class User
{
// Intentionally a very simplified DTO class
public string Name { get; set; }
public bool IsAdmin { get; set; }
}
Some class that works with some users. Note no user is an Admin.
public class SomeClassWithUsers
{
public List<User> UserList1 { get; private set; }
private List<User> _users = new List<User>();
public IReadOnlyList<User> UserList2 => _users;
public static SomeClassWithUsers CreateSample()
{
var x = new SomeClassWithUsers();
x.CreateSampleUsers();
return x;
}
public void CreateSampleUsers()
{
_users = new List<User>()
{
new User() {Name = "Alice", IsAdmin = false },
new User() {Name = "Bob", IsAdmin = false },
new User() {Name = "Carl", IsAdmin = false },
new User() {Name = "Dan", IsAdmin = false },
new User() {Name = "Eve", IsAdmin = false },
};
UserList1 = _users.ToList(); // independent copy
}
}
Okay, so we have 2 different user lists. Are both of them protected from external changes? No. Even though UserList1 has a private setter, someone can still alter individual items.
Example:
static void Main(string[] args)
{
var x = SomeClassWithUsers.CreateSample();
// Even though UserList1 has a private setter, I can still change individual members.
for (var i = 0; i < x.UserList1.Count; i++)
{
x.UserList1[i] = new User() { Name = $"Evil {x.UserList1[i].Name}", IsAdmin = true };
}
Console.WriteLine("UserList1 has been modifed!");
foreach (var user in x.UserList1)
{
Console.WriteLine($"{user.Name} {(user.IsAdmin ? "IS" : "is NOT")} an Admin.");
}
// But I cannot altger UserList2 in any way since it is properly marked as a IReadOnlyList.
// You cannot compile the code below.
//for (var i = 0; i < x.UserList2.Count; i++)
//{
// x.UserList2[i] = new User() { Name = $"Evil {x.UserList1[2].Name}", IsAdmin = true };
//}
Console.WriteLine("\nUserList2 remains unchanged.");
foreach (var user in x.UserList2)
{
Console.WriteLine($"{user.Name} {(user.IsAdmin ? "IS" : "is NOT")} an Admin.");
}
Console.WriteLine("\nPress ENTER key to close");
Console.ReadLine();
}
Console output:
UserList1 has been modifed!
Evil Alice IS an Admin.
Evil Bob IS an Admin.
Evil Carl IS an Admin.
Evil Dan IS an Admin.
Evil Eve IS an Admin.
UserList2 remains unchanged.
Alice is NOT an Admin.
Bob is NOT an Admin.
Carl is NOT an Admin.
Dan is NOT an Admin.
Eve is NOT an Admin.
Press ENTER key to close