如何在不使用C#创建副本的情况下获取对列表的一部分的引用

时间:2017-02-17 08:00:54

标签: c# list collections

我有一个派生自List<T>的集合类,需要在该集合的实例上执行打包的action(即集合太大,所以我想将集合拆分为多个部分,执行部件上的action。请注意,action有一个类型化的签名,它是一些预定义的方法,它需要一个相同类型的集合。

我知道

target.addRange(source.getRange(start, packageSize);

但这不是我想要的,因为为此我需要一个新的集合实例,并将创建列表条目的副本。因为我知道动作不会以任何方式操纵列表,所以我更喜欢做

之类的事情
action(source.Reference(fromIndex, toIndex);

意图不创建列表条目的副本(我知道这些是'仅'参考,但仍有副本。有没有办法在C#中做到这一点?

当然我可以将方法传递给知道如何检索范围的action,但是操作不需要知道我将执行细分为部分的意图。

5 个答案:

答案 0 :(得分:2)

您可以使用LINQ或简单的C#从列表中获取部分,而不是将引用的副本复制到新列表中。

使用SkipTake

var iterator = source.Skip(start).Take(end - start);

由于它是IEnumerable,因此您可以在调用的方法中foreach以上(因此您可能需要更改方法签名):

foreach (var x in iterator)
{
    ...
}

或者创建自己的状态机。生成的可枚举也可以传递给方法:

private static IEnumerable<T> Take<T>(List<T> source, int start, int end)
{
    for (int i = start; i < end; i++)
    {
        yield return source[i];
    }
}

答案 1 :(得分:1)

只需使用Linq:

var notAList = source.Skip(fromIndex).Take(toIndex-fromIndex);
action(notAList);

通过这种方式,您可以创建枚举器,该枚举器将枚举您的数字,而无需为所有数据实例化空间。

答案 2 :(得分:1)

您可以使用LINQ的Skip()Take()

public void MyAction<T>(IEnumerable<T> range)
{
    // ...
}

并致电

MyAction(list.Skip(fromIndex).Take(toIndex-fromIndex));

请注意,此运算符(SkipTake)使用延迟执行并评估延迟。所以你确实可以把它想象成源列表中范围的一种引用

答案 3 :(得分:1)

您可以创建自己的迭代器:

public class MyPartListIterator: IEnumerator
{
    private readonly IList list;
    private readonly int _startIdx;
    private readonly int _endIdx;
    private int _current;

    public MyIterator(IList list, int start, int end)
    {
        //do some validations before etc
        this._startIdx = this._current =  start;
        this._endIdx = end;
        this._list=  list;

    }

    public bool MoveNext() 
    {
       //do some checks against list was changed

       if (this._current >= this._endIdx) return false;
       this._current += 1;
    }

    public object Current => this._list[this._current];

    public void Reset() => this._current = this._startidx;
}

然后你可以将迭代器的实例传递给你的action

为什么它优于SkipTake?它在启动索引之前不会枚举元素。

您还可以准备课程的通用版本。

从记忆中走出来,没有经过测试。使用C#6.0语法糖。

修改

无法将IList<T>的相同引用传递给您的大ILIst<T>且仅包含源列表的部分数据。

答案 4 :(得分:0)

您可以在此处使用扩展程序来获取您正在寻找的功能。

public static class ListExtensions
{
    public static void PerformActionOnSubset<T>(this IList<T> collection, int fromIndex,
        int toIndex, Action<IList<T>> action)
    {
        action(collection.Skip(fromIndex).Take(toIndex - fromIndex).ToList());
    }
}

myCollectionOfSomethings.PerformActionOnSubset(10, 30, myAction);