我怎样才能安全返回列表< T>从方法/属性声明为IEnumerable< T&GT ;? [英] How can I safely return List<T> from method/property declared as IEnumerable<T>?

查看:136
本文介绍了我怎样才能安全返回列表< T>从方法/属性声明为IEnumerable< T&GT ;?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

让我们说我有的IEnumerable< INT> 属性的支持与列表< INT> 字段,这样我就可以修改从类中集,但它公开公开为只读。

 公共类Foo 
{
私有列表< INT> _bar =新的List< INT>();

公开的IEnumerable< INT>酒吧
{
{返回_bar; }
}
}



但像您可以轻松地投对象代码从属性回到提取到列表< INT> 并修改它:

 无功富=新的Foo(); 
VAR栏=(列表< INT>)foo.Bar;
bar.Add(10);



问题是:什么是最好的(最可读的,最简单的写,没有性能损失?)的方式,以避免



我可以拿出至少4个解决方案,但其中非是完美的:




  1. 的foreach 收益回报率

     公开的IEnumerable< INT>酒吧
    {
    得到
    {
    的foreach(在_bar VAR项)
    收益回报的项目;
    }
    }



    - 真烦人写入和读取。


  2. AsReadOnly()

     公开的IEnumerable< INT>酒吧
    {
    {返回_bar.AsReadOnly(); }
    }



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


  3. 了ToList()

     公开的IEnumerable< INT>酒吧
    {
    {返回_bar.ToList(); }
    }



    + 用户仍可以修改检索收集,但它不是我们从类中修改同一个集合,所以我们不应该在乎。结果
    - 创建整个集合的副本,这可能会使问题时,收集大


  4. 自定义<。 。EM>包装的类

     公共静态类MyExtensions 
    {
    类的私有MyEnumerable< T> :IEnumerable的< T>
    {
    私人的ICollection< T> _资源;
    公共MyEnumerable(ICollection的< T>源)
    {
    _source =来源;
    }

    公众的IEnumerator< T>的GetEnumerator()
    {
    返回_source.GetEnumerator();
    }

    的IEnumerator IEnumerable.GetEnumerator()
    {
    回报率((IEnumerable的)_source).GetEnumerator();
    }
    }

    公共静态的IEnumerable< T> AsMyEnumerable< T>(这ICollection的< T>源)
    {
    返回新MyEnumerable< T>(源);
    }
    }



    用法:

     公开的IEnumerable< INT>酒吧
    {
    得到
    {
    返回_bar.AsMyEnumerable();
    }
    }



    + 不要需要克隆的收集结果
    - 当你使用它作为LINQ查询源的一些方法不使用 ICollection.Count ,因为你不公开它。







?有没有更好的方式来做到这一点


解决方案

问题是:什么是最好的(最好的可读性,最容易写,没有性能损失)的方式,以避免?




在一般情况下,我不会尝试以避免它。我的API的消费者应该用我揭露的类型,如果他们不这样做,而产生的任何错误是他们的错,不是我的。因此,我真的不关心,如果他们投了数据的方式 - 当我改变我的内部表示,他们获得投例外,那是他们的问题。



这。虽这么说,如果有一个安全问题,我可能会只使用 AsReadOnly 。这是有效的自我记录,并没有真正的缺点(除了为包装的小分配,因为没有数据副本,你得到的改性有意义的异常等)。有没有真正的缺点与这使自己的自定义包装,和一个自定义的包装意味着更多的代码来测试和维护。



在一般情况下,我个人尽量避免复制的没有道理的。这将消除了ToList()作为一般的选项。使用迭代器(你的第一个选项)是不是那么糟糕,虽然它并没有真正超过 ReadOnlyCollection还和LT提供了许多优势; T>


Let's say I have IEnumerable<int> property backed with List<int> field, so I can modify the collection from within the class, but it's publicly exposed as read-only.

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

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

But with code like that you can easily cast object retrieved from the property back to List<int> and modify it:

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

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

I can come up with at least 4 solutions, but non of them is perfect:

  1. foreach and yield return:

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

    - really annoying to write and to read.

  2. AsReadOnly():

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

    + will cause exception when someone tries to modify the returned collection
    + does not create a copy of the entire collection.

  3. ToList()

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

    + User can still modify retrieved collection, but it's not the same collection we are modifying from within the class, so we shouldn't care.
    - creates a copy of entire collection what may cause problems when collection is big.

  4. Custom wrapper class.

    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);
        }
    }
    

    usage:

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

    + don't need to clone the collection
    - when you use it as LINQ queries source some methods won't use ICollection.Count, because you don't expose it.


Is there any better way to do that?

解决方案

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

In general, I don't try to avoid it. The consumer of my API should use the type I expose, and if they don't, any bugs resulting are their fault, not mine. As such, I don't really care if they cast the data that way - when I change my internal representation, and they get cast exceptions, that's their issue.

That being said, if there is a security concern, I would likely just use AsReadOnly. This is effectively self-documenting, and has no real downsides (apart from a small allocation for the wrapper, as there is no copy of the data, you do get meaningful exceptions on modification, etc). There is no real disadvantage to this vs. making your own custom wrapper, and a custom wrapper means more code to test and maintain.

In general, I personally try to avoid copying without reason. That would eliminate ToList() as an option in general. Using an iterator (your first option) is not as bad, though it does not really provide many advantages over a ReadOnlyCollection<T>.

这篇关于我怎样才能安全返回列表&LT; T&GT;从方法/属性声明为IEnumerable&LT; T&GT ;?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆