【问题标题】:Why would one use IEnumerable<T> in an interface definition if the class implementing the interface will be using List<T>?如果实现接口的类将使用 List<T>,为什么还要在接口定义中使用 IEnumerable<T>?
【发布时间】:2014-02-06 17:27:54
【问题描述】:

我目前正在学习位于here 的 .NET Web API 教程。在示例中,我们在模型类中定义了一个接口,如下所示:

namespace ProductStore.Models
{
    public interface IProductRepository
    {
        IEnumerable<Product> GetAll();
        Product Get(int id);
        Product Add(Product item);
        void Remove(int id);
        bool Update(Product item);
    }
}

然后,在本教程的后面,我们这样实现这个接口:

namespace ProductStore.Models
{
    public class ProductRepository : IProductRepository
    {
        private List<Product> products = new List<Product>();
        private int _nextId = 1;

        public ProductRepository()
        {
            Add(new Product { Name = "Tomato soup", Category = "Groceries", Price = 1.39M });
            Add(new Product { Name = "Yo-yo", Category = "Toys", Price = 3.75M });
            Add(new Product { Name = "Hammer", Category = "Hardware", Price = 16.99M });
        }

        public IEnumerable<Product> GetAll()
        {
            return products;
        }

        public Product Get(int id)
        {
            return products.Find(p => p.Id == id);
        }

        public Product Add(Product item)
        {
            if (item == null)
            {
                throw new ArgumentNullException("item");
            }
            item.Id = _nextId++;
            products.Add(item);
            return item;
        }

        public void Remove(int id)
        {
            products.RemoveAll(p => p.Id == id);
        }

        public bool Update(Product item)
        {
            if (item == null)
            {
                throw new ArgumentNullException("item");
            }
            int index = products.FindIndex(p => p.Id == item.Id);
            if (index == -1)
            {
                return false;
            }
            products.RemoveAt(index);
            products.Add(item);
            return true;
        }
    }
}

我的问题是,如果实现要专门在List&lt;Product&gt; 上运行,为什么要编写接口以使GetAll() 返回IEnumerable&lt;Product&gt;

我假设这是为了促进重用,以便其他一些IProductRepository 实现可以使用不同的IEnumerable&lt;Product&gt;(尽管这样的例子很好,因为我想不出一个,并不是我怀疑它存在,我只是经验不足)。

假设重用确实是目标,那么为什么要这样写实现:

public IEnumerable<Product> GetAll()
{
    return products;
}

而不是这样:

public List<Product> GetAll()
{
    return products;
}

框架或编译器是否无法看到public List&lt;Product&gt; GetAll() 可从public IEnumerable&lt;Product&gt; GetAll() 派生?

【问题讨论】:

  • 因为这意味着您将来可以将其更改为 List&lt;T&gt; 以外的其他内容,而不会破坏接口契约。

标签: c# asp.net asp.net-web-api ienumerable


【解决方案1】:

两个主要原因:

  • 这隐藏了实际的实现,这样消费者就不会对数据做任何他们不应该做的事情(例如,直接从列表中添加或删除项目)。显然,消费者可以将结果转换为 List&lt;T&gt;,但他们将依赖于未记录的实现细节,因此如果您稍后更改它并破坏他们的代码,那将是他们的错。
  • 如果有一天您需要更改实现以使用 List&lt;T&gt; 以外的其他内容,您可以在没有任何可见更改的情况下执行此操作。

【讨论】:

    【解决方案2】:

    您希望对 List&lt;T&gt; 的更改通过您的存储库方法进行操作,而不是让用户直接访问 List&lt;T&gt; - 因此,您返回一个 IEnumerable&lt;T&gt; 以便他们无法更改基础集合

    【讨论】:

    • 用户仍然可以将值转换为 List,这不是真的吗?
    • 另请注意,有一条代码分析规则不建议这样做:Do not expose generic lists
    • stuartd, list.AsReadOnly() 将防止演员被黑
    • @stuard:一个接口定义了一个契约,如果你尊重它,你就确定它是有效的。如果没有,您将承担潜在问题的风险。将 IEnumerable 转换为更专业的类型会破坏合同...
    • @bubbleking 我想我是直接从谷歌那里得到的。这个版本可能会更有帮助:msdn.microsoft.com/en-us/library/ms182142(v=vs.120).aspx
    猜你喜欢
    • 2014-03-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-04-04
    • 2011-04-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多