c# - 如何从声明为 IEnumerable<T> 的方法/属性安全地返回 List<T>?

标签 c# list ienumerable

假设我有 IEnumerable<int>List<int> 为后盾的属性(property)字段,因此我可以在类中修改集合,但它公开显示为只读。

public class Foo
{
    private List<int> _bar = new List<int>();

    public IEnumerable<int> Bar
    {
        get { return _bar; }
    }
}

但是使用这样的代码,您可以轻松地将从属性中检索到的对象转换回 List<int>并修改它:

var foo = new Foo();
var bar = (List<int>)foo.Bar;
bar.Add(10);

问题是:什么是最好的(最好的可读性、最容易编写的、没有性能损失的)方法来避免这种情况?

我可以想出至少 4 个解决方案,但没有一个是完美的:

  1. foreachyield return :

    public IEnumerable<int> Bar
    {
        get
        {
            foreach (var item in _bar)
                yield return item;
        }
    }
    

    - 写和读真的很烦人。

  2. AsReadOnly() :

    public IEnumerable<int> Bar
    {
        get { return _bar.AsReadOnly(); }
    }
    

    + 当有人试图修改返回的集合时会导致异常
    + 不会创建整个集合的副本。

  3. ToList()

    public IEnumerable<int> Bar
    {
        get { return _bar.ToList(); }
    }
    

    + 用户仍然可以修改检索到的集合,但它不是我们在类中修改的同一个集合,所以我们不应该在意。
    - 创建整个集合的副本,当集合很大时可能会导致问题。

  4. 自定义包装器类。

    public static class MyExtensions
    {
        private class MyEnumerable<T> : IEnumerable<T>
        {
            private ICollection<T> _source;
            public MyEnumerable(ICollection<T> source)
            {
                _source = source;
            }
    
            public IEnumerator<T> GetEnumerator()
            {
                return _source.GetEnumerator();
            }
    
            IEnumerator IEnumerable.GetEnumerator()
            {
                return ((IEnumerable)_source).GetEnumerator();
            }
        }
    
        public static IEnumerable<T> AsMyEnumerable<T>(this ICollection<T> source)
        {
            return new MyEnumerable<T>(source);
        }
    }
    

    用法:

    public IEnumerable<int> Bar
    {
        get
        {
            return _bar.AsMyEnumerable();
        }
    }
    

    +不需要克隆集合
    - 当您将它用作 LINQ 查询源时,某些方法不会使用 ICollection.Count ,因为您不公开它。


有更好的方法吗?

最佳答案

Question is: what is the best (best readable, easiest to write, without performance loss) way to avoid that?

一般来说,我不会试图回避它。我的 API 的使用者应该使用我公开的类型,如果他们不这样做,那么由此产生的任何错误都是他们的错,而不是我的错。因此,我真的不在乎他们是否以这种方式转换数据 - 当我更改我的内部表示时,他们得到转换异常,那是他们的问题。

也就是说,如果存在安全问题,我可能只使用 AsReadOnly .这是有效的 self 记录,并没有真正的缺点(除了包装器的小分配,因为没有数据副本,你确实会在修改时得到有意义的异常等)。与制作您自己的自定义包装器相比,这并没有真正的缺点,并且自定义包装器意味着需要测试和维护更多代码。

一般来说,我个人尽量避免无故复制。这将消除 ToList()作为一般的选择。使用迭代器(您的第一个选项)并没有那么糟糕,尽管它并没有真正提供比 ReadOnlyCollection<T> 很多优势。 .

关于c# - 如何从声明为 IEnumerable<T> 的方法/属性安全地返回 List<T>?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20984980/

相关文章:

c# - 如何检测 IEnumerable<T> 中的 "missing"元素?

c# - 实现 IEnumerable<T> 和 IEnumerator<T> 时 GetEnumerator() 的推荐行为

c# - 在 ASP.NET Web 应用程序中突出显示菜单栏

c# - iOS 使图像适合屏幕

python - 比较python中的两个大列表

list - 如何在数独游戏的序言列表中设置值

c# - 测试类型是否为集合的最佳方法

c# - 消除代码隐藏中的 Storyboard Completed 处理程序

c# - 如何使用 C# 将 JSON 发布到服务器?

r - Pander+Knitr : Error when using pander. 列表问题