IEnumerable现在比IReadOnlyList更加防水吗?

时间:2018-04-16 12:29:38

标签: c# domain-driven-design

我已阅读此问题:IEnumerable vs IReadonlyCollection vs ReadonlyCollection for exposing a list member此问题:ReadOnlyCollection or IEnumerable for exposing member collections?。我很有意思这些答案是几年前写的。

Jon Skeet的回答表明我应该使用IReadOnlyCollection而不是IEnumerable,因为我非常谨慎地阻止这样的代码:

ICollection<Order> evil = (ICollection<Order>)customer.Orders;
evil.Add(order3);

现在看下面的代码:

public class Customer
{
    private readonly List<Order> _orders = new List<Order>();

    public string FirstName { get; set; }
    public string LastName { get; set; }
    public IEnumerable<Order> Orders
    {
        get { foreach (var order in _orders) yield return order; }
    }

    internal void AddOrder(Order order)
    {
        _orders.Add(order);
    }

    public void AddOrders(IEnumerable<Order> orders)
    {
        this._orders.AddRange(orders);
    }
}

我可以使用这样的代码:

Order order1 = new Order();
Order order2 = new Order();
Order order3 = new Order();

Customer customer = new Customer();
customer.FirstName = "Fred";
customer.LastName = "Bloggs";
customer.AddOrder(order1);
customer.AddOrder(order2);

并且像这样:

Customer customer2 = new Customer();
customer2.FirstName = "Average";
customer2.LastName = "Joe";

List<Order> orders = new List<Order>();
Order order4 = new Order();
Order order5 = new Order();
orders.Add(order4);
orders.Add(order5);
customer2.AddOrders(orders);

使用此代码有任何风险吗?我试图尽可能地保持水密,因为它是公共API的一部分。这比IReadOnlyCollection更加防水吗?效率低下吗?请注意,这是代码的最终版本,因此没有其他方案需要考虑。

我担心的是:

  1. 公开IEnumerable而不是只读列表。

  2. 使用列表私下保存订单。

  3. 我正尽力遵循此处所述的建议:https://docs.microsoft.com/en-us/dotnet/standard/design-guidelines/guidelines-for-collections

2 个答案:

答案 0 :(得分:2)

  

这比IReadOnlyCollection更加防水吗?

我不这么认为。我会说他们以另一种方式服务于同一目的。

  

使用此代码有任何风险吗?它的效率会降低吗?

风险?不是我知道的。由于yield return编译为内部状态机,因此您将失去列表的直接曝光。这是您的方法的一个好处,就像只读集合一样。

您的代码确实会对性能产生影响:获取计数和其他相关属性的复杂度为O(n)而不是O(1)。这可能会损害您的表现。

答案 1 :(得分:1)

我会说:

public IReadOnlyList<Order> Orders => _orders.AsReadOnly();

它将你的List包装成只读包装器,所以这样的“邪恶”代码:

ICollection<Order> evil = (ICollection<Order>)customer.Orders;
evil.Add(order3);

只会抛出一个例外,即colleciton是只读的。使用IEnumerable对这种情况具有性能和可用性含义,但没有提供太多好处(我会说)。

例如,我想这是获得大量客户订单的常见操作。如果您返回IEnumerable - 与IReadOnlyList相比,该操作的性能较差且对调用者来说不太方便。

性能较差是因为使用IEnumerable获取订单数量的唯一方法是Count() LINQ方法,而这种方法在使用yield return的实现中必须通过整个收集和计算有多少项目。使用IReadOnlyListCount属性只包含该信息。

不太方便,因为不鼓励使用单个可枚举的多个枚举,VS会警告用户你的api。假设您的api用户的代码如下:

IEnumerable<Order> orders = customer2.Orders;
// just an example, there is actually no need to check for count
// before enumerating
if (orders.Count() > 0) {
    foreach (var order in orders) {

    }
}

foreach行,VS会警告他“可能有多个IEnumerable枚举”。调用者对IEnumerable所能做的唯一合理的事就是枚举它。一旦。因此,为了避免这种警告,他必须编写一些丑陋的代码,或者执行var orders = customer.Orders.ToList(),创建不必要的列表副本并使用它。

没有理由限制你的api用户,除非枚举一次是使用该api的唯一正确方法。