提问人:Heinzi 提问时间:6/2/2020 最后编辑:Heinzi 更新时间:6/10/2020 访问量:302
具有默认值的对象初始值设定项内的集合初始值设定项
Collection initializers inside object initializers with default values
问:
我只是偶然发现了以下问题:
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
}
我理解为什么会发生这种情况:不是赋值,而是对象初始值设定项中的集合初始值设定项,即它是 的隐式调用。AllowedIds = { 1, 2 }
AllowedIds.Add(1); AllowedIds.Add(2)
不过,对我来说,这是一个陷阱,因为它看起来像一个作业(因为它使用 ).=
作为一个 API/库开发人员(假设我是开发类的人),想要坚持最小意外原则,我能做些什么来防止我的库的使用者落入这个陷阱吗?Settings
脚注:
在这种特殊情况下,我可以使用代替(因为重复项对 没有意义),这将产生 的预期结果。尽管如此,初始化仍会产生违反直觉的结果。
ISet/HashSet<int>
ICollection/List
AllowedIds
1, 2
AllowedIds = { 2 }
1, 2
我在 C# github 存储库上找到了一个相关的讨论,它基本上得出的结论是,是的,这个语法令人困惑,但它是一个旧功能(于 2006 年引入),如果不破坏向后兼容性,我们就无法更改它。
答:
如果您不希望类的用户添加到,为什么要将其公开为(其中包含一个方法并表示要添加到的意图)?Settings
AllowedIds
ICollection<int>
Add
之所以在代码中起作用,是因为 C# 使用 duck 类型来调用集合上的 Add
方法。如果排除编译器找到方法的可能性,则行上将出现编译错误,从而防止陷阱。AllowedIds = { 1, 2 }
Add
AllowedIds = { 1, 2 }
您可以执行以下操作:
class Settings
{
// Let's set some default value: { 1 }
public IEnumerable<int> AllowedIds { get; set; } = new List<int> { 1 };
}
static void Main(string[] args)
{
var s = new Settings
{
AllowedIds = new List<int> { 1, 2 }
};
Console.WriteLine(string.Join(", ", s.AllowedIds)); // prints 1, 2
}
这样,您仍然允许调用方使用 setter 设置新的集合,同时防止您提到的陷阱。
评论
AddAllowedId
Main
IEnumerable <int>
Settings
AddAllowedId
AllowedIds
ICollection<int>
评论