I just stumbled upon the following issue:
class Settings
{
// Let's set some default value: { 1 }
public ICollection<int> AllowedIds = new List<int>() { 1 };
}
static void Main(string[] args)
{
var s = new Settings
{
AllowedIds = { 1, 2 }
};
Console.WriteLine(string.Join(", ", s.AllowedIds)); // prints 1, 1, 2
}
I understand why this happens: AllowedIds = { 1, 2 } is not an assignment but a collection initializer inside an object initializer, i.e., it's an implicit call of AllowedIds.Add(1); AllowedIds.Add(2).
Still, for me it was a gotcha, since it looks like an assignment (since it uses =).
As an API/library developer (let's say I'm the one developing the Settings class) who wants to adhere to the principle of least surprise, is there anything I can do to prevent the consumers of my library from falling into that trap?
Footnotes:
In that particular case, I could use an
ISet/HashSet<int>instead ofICollection/List(since duplicates do not make sense forAllowedIds), which would yield the expected result of1, 2. Still, initializingAllowedIds = { 2 }would yield the counter-intuitive result of1, 2.I found a related discussion on the C# github repo, which basically concluded that, yes, this syntax is confusing, but it's an old feature (introduced in 2006), and we can't change it without breaking backwards compatibility.