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.