IEnumerable <T> vs. Array

Mat*_*ler 24 c# linq arrays data-structures

我想要得到的想法,这将是发布对象的只读目录作为一个公共方法的最佳方式?从Eric Lippert的博客来看,阵列有点糟糕,因为有人可以轻松添加新条目.因此,每次调用该方法时都必须传递一个新数组.他建议,要通过IEnumerable<T>,因为这是每个定义只读(没有添加,删除方法),我练习了很长一段时间.但是在我们的新项目中,人们甚至开始创建这些的数组IEnumerables,因为他们不知道后面的DataSource,所以他们得到:处理警告可能多次枚举IEnumerable

我对技术方法感兴趣,如何解决这个难题.到目前为止,我提出的唯一解决方案是使用a IReadOnlyCollection,但这比使用更明确IEnumerable.

发布此类列表的最佳做法是什么,这些列表不应更改,但应声明为内存列表?

Mat*_*zer 14

通常 - 并且从那时起 - 这解决了使用不可变集合.

例如,您的公共属性应该是类型IImmutableList<T>,IImmutableHashSet<T>等等.

任何IEnumerable<T>都可以转换为不可变集合:

  • someEnumerable.ToImmutableList();
  • someEnumerable.ToImmutableHashSet();
  • ...... 等等.

这样,您可以使用可变集合处理私有属性,并仅提供不可变集合的公共表面.

例如:

public class A
{
     private List<string> StringListInternal { get; set; } = new List<string>();
     public IImmutableList<string> StringList => StringListInternal.ToImmutableList();
}
Run Code Online (Sandbox Code Playgroud)

还有一种使用接口的替代方法:

public interface IReadOnlyA
{
     IImmutableList<string> StringList { get; }
}

public class A : IReadOnlyA
{
     public List<string> StringList { get; set; } = new List<string>();
     IImmutableList<string> IReadOnlyA.StringList => StringList.ToImmutableList();
}
Run Code Online (Sandbox Code Playgroud)

检查IReadOnlyA 已经显式实现,因此可变StringList属性和不可变属性可以作为同一个类的一部分共存.

当你想暴露一个不可变的时候A,那么你将你的A对象返回到IReadOnlyA上层,而上面的层将无法StringList在上面的示例中改变整体:

public IReadOnlyA DoStuff()
{
     return new A();
}

IReadOnlyA a = DoStuff();

// OK! IReadOnly.StringList is IImmutableList<string>
IImmutableList<string> stringList = a.StringList;
Run Code Online (Sandbox Code Playgroud)

每次都避免将可变列表转换为不可变列表

它应该是一种可能的解决方案,以避免每次访问不可变的列表时将源列表转换为不可变列表.

等同的成员

如果项的类型覆盖Object.EqualsGetHashCode(可选)实现IEquatable<T>,则公共不可变列表属性访问可能如下所示:

public class A : IReadOnlyA
{
     private IImmutableList<string> _immutableStringList;

     public List<string> StringList { get; set; } = new List<string>();

     IImmutableList<string> IReadOnlyA.StringList
     {
         get
         {
             // An intersection will verify that the entire immutable list
             // contains the exact same elements and count of mutable list
             if(_immutableStringList.Intersect(StringList).Count == StringList.Count)
                return _immutableStringList;
             else
             {
                  // the intersection demonstrated that mutable and
                  // immutable list have different counts, thus, a new
                  // immutable list must be created again
                 _immutableStringList = StringList.ToImmutableList();

                 return _immutableStringList;
             }
         }
     }
}
Run Code Online (Sandbox Code Playgroud)


Ant*_*sek 6

我不认为不可改变是要走的路

int[] source = new int[10000000];//uses 40MB of memory
var imm1 = source.ToImmutableArray();//uses another 40MB
var imm2 = source.ToImmutableArray();//uses another 40MB
Run Code Online (Sandbox Code Playgroud)

列表的行为方式相同.如果我想每次都进行完整复制,我不必关心用户对该数组的处理方式.使其不可变不会保护集合中对象的内容,它们可以自由更改.@HansPassant建议似乎是最好的

public class A
{
    protected List<int> list = new List<int>(Enumerable.Range(1, 10000000));
    public IReadOnlyList<int> GetList
    {
        get { return list; }
    }
}
Run Code Online (Sandbox Code Playgroud)

  • @MatíasFidemraizer"阵列有点不好,因为有人可以轻松添加一个新的条目.因此,每次调用该方法时都必须传递一个新的数组." OP是谁想要在每次通话中避免这种创造,而不是我.所以问题是,如果可以避免用户更改集合而没有问题,你会继续说,不要避免它并不总是这样的问题. (2认同)

D S*_*ley 5

对于您不打算修改的集合,IEnumerable<T>仍然可能是最安全的选项,而且它允许任何集合类型,而不仅仅是数组.这种警告的原因是因为IEnumerable代表使用延迟执行的查询的可能性,这意味着可能多次执行可能很昂贵的操作.
请注意,没有一个接口可以区分内存中的集合与可能的延迟执行包装器. 之前已经问过这个问题.

为修复不多次枚举源.如果代码需要执行多次迭代(这可能是合法的),那么将集合水合到迭代List<T> 之前.


Ami*_*rzi 5

IEnumerable是一个只读界面,隐藏了用户的实现.有人可以争辩说,用户可以转换IEnumerable为列表并添加新项目,但这意味着两件事:

  • 用户违反了提供的API
  • 您无法阻止用户使用反射

IEnumerable描述行为,同时List是该行为的实现.当您使用时IEnumerable,您可以让编译器有机会将工作推迟到以后,可能会在此过程中进行优化.如果您使用ToList(),则强制编译器立即准备结果.

我使用IEnumerable每当使用LINQ表达式时,因为仅通过指定行为,我给LINQ提供了推迟评估并可能优化程序的机会.

要防止对List<T>对象进行任何修改,只能通过ReadOnlyCollection<T>不公开修改集合的方法的包装器公开它.但是,如果对基础List<T>对象进行了更改,则只读集合将反映MSDN中所述的那些更改.

查看.NET 4.5的新只读集合.