对象初始化器内的集合初始化器,具有默认值。

问题描述 投票:1回答:1

我只是偶然发现了以下问题。

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).

不过,对我来说,这也是一个陷阱,因为它 样子 像赋值一样(因为它使用了 =).

作为一个APIlibrary开发者(比方说,我是开发APIlibrary的人。Settings 类)的人,谁愿意坚持 最低限度原则, 有什么办法可以防止我库的消费者掉进这个陷阱吗?


脚注。

  • 在这种特殊情况下,我可以使用 ISet/HashSet<int> 而不是 ICollection/List (因为重复的东西对 AllowedIds),从而得出预期结果为 1, 2. 不过,初始化 AllowedIds = { 2 } 会产生以下反直觉的结果 1, 2.

  • 我发现了一个相关的讨论 C# github repo,它的基本结论是,是的,这种语法很混乱,但它是一个老的功能(2006年引入),我们不能在不破坏向后兼容性的情况下改变它。

c# api-design collection-initializer least-astonishment
1个回答
0
投票

如果你不希望用户的? Settings 班级 增加AllowedIds,为什么要把它作为一个 ICollection<int> (其中包含一个 Add 方法,并表示要加进的意图)?)

原因是 AllowedIds = { 1, 2 } 在您的代码中工作是因为C#使用了鸭式输入法,以 称呼 Add 集合上的. 如果你消除了编译器发现一个 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设置一个新的集合, 同时防止你提到的陷阱.

© www.soinside.com 2019 - 2024. All rights reserved.