C# 林克在拍摄后继续拍摄

C# 林克在拍摄后继续拍摄,c#,linq,C#,Linq,假设我们有一个IEnumerable的东西 有没有一种简洁的方法可以在不重新计算的情况下,先取n个元素,然后在第一个元素之后再取m个元素 示例代码: stuff.Take(10); stuff.Skip(10).Take(20); // re-evaluates stuff 我当时想的可能是这个(不是工作代码) 编辑以增加难度并澄清我想完成的任务的复杂性:我想在完成任务后继续查询,即 it.Take(10); var cont = it.Select(Mutate); cont.Take(20

假设我们有一个
IEnumerable的东西

有没有一种简洁的方法可以在不重新计算的情况下,先取n个元素,然后在第一个元素之后再取m个元素

示例代码:

stuff.Take(10);
stuff.Skip(10).Take(20); // re-evaluates stuff
我当时想的可能是这个(不是工作代码)

编辑以增加难度并澄清我想完成的任务的复杂性:我想在完成任务后继续查询,即

it.Take(10);
var cont = it.Select(Mutate);
cont.Take(20);
cont = cont.Where(Filter);
cont.Take(5);

您可以使用Microsoft推出的
System.Interactive
NuGet包中的
Publish
扩展方法来完成此任务。这是一个很棒的库,提供了一些“缺失的”LINQ函数。从文档中,发布方法为:

创建一个具有源序列视图的缓冲区,使每个枚举器从缓冲区中的当前索引获取对序列其余部分的访问

也就是说,它允许您部分枚举一个序列,下次枚举该序列时,您将从上一次枚举结束的地方开始

var publishedSource = stuff.Publish();

var firstTenItems = publishedSource.Take(10).ToArray();
var nextTwentyTransformedItems = publishedSource.Take(20).Select(Mutate).ToArray();
// How you apply 'Where' depends on what you want to achieve.
// This returns the next 5 items that match the filter but if there are less
// than 5 items that match the filter you could end up enumerating the
// entire remainder of the sequence.
var nextFiveFilteredItems = publishedSource.Where(Filter).Take(5).ToArray(); 
// This enumerates _only_ the next 5 items and yields any that match the filter.
var nextOfFiveItemsThatPassFilter = publishedSource.Take(5).Where(Filter).ToArray()

如果您只想为
IEnumerable
创建一个包装器,该包装器将处理附加在其上的任何LINQ并对源代码进行一次遍历,请使用以下类和扩展:

public static class EnumerableOnceExt {
    public static EnumerableOnce<IEnumerable<T>, T> EnumerableOnce<T>(this IEnumerable<T> src) => new EnumerableOnce<IEnumerable<T>, T>(src);
}

public class EnumerableOnce<T, V> : IEnumerable<V>, IDisposable where T : IEnumerable<V> {
    EnumeratorOnce<V> onceEnum;

    public EnumerableOnce(T src) {
        onceEnum = new EnumeratorOnce<V>(src.GetEnumerator());
    }

    public IEnumerator<V> GetEnumerator() {
        return onceEnum;
    }

    IEnumerator IEnumerable.GetEnumerator() {
        return onceEnum;
    }

    public void DoSkip(int n) {
        while (n > 0 && onceEnum.MoveNext())
        --n;
    }

    public void DoTake(int n) {
        while (n > 0 && onceEnum.MoveNext())
            --n;
    }

    #region IDisposable Support
    private bool disposedValue = false; // To detect redundant calls

    protected virtual void Dispose(bool disposing) {
        if (!disposedValue) {
            if (disposing) {
                onceEnum.ActuallyDispose();
            }

            disposedValue = true;
        }
    }

    // This code added to correctly implement the disposable pattern.
    public void Dispose() {
        Dispose(true);
    }
    #endregion
}

public class EnumeratorOnce<V> : IEnumerator<V> {
    IEnumerator<V> origEnum;

    public EnumeratorOnce(IEnumerator<V> src) {
        origEnum = src;
    }

    public V Current => origEnum.Current;

    object IEnumerator.Current => origEnum.Current;

    public bool MoveNext() => origEnum.MoveNext();

    public void Reset() {
        origEnum.Reset();
    }

    public void ActuallyDispose() {
        origEnum.Dispose();
    }

    #region IDisposable Support
    protected virtual void Dispose(bool disposing) {
        // don't allow disposing early
    }

    // This code added to correctly implement the disposable pattern.
    public void Dispose() {
        Dispose(true);
    }
    #endregion
}
您还可以将新方法添加到
enumerablence

public void DoSkip(int n) {
    while (n > 0 && srcEnum.MoveNext())
    --n;
}

public void DoTake(int n) {
    while (n > 0 && srcEnum.MoveNext())
        --n;
}
并称之为:

var it1 = it.EnumerableOnce();
it1.DoTake(10);
var @continue = it1.Select(Mutate);
@continue.DoSkip(20);
@continue = @continue.Where(Filter);
@continue.DoTake(5);

你可以说你所联系的问题是相关的,但是那些答案不是。大多数都是“自己动手”的变体,带有糟糕的代码示例。
var temp=stuff.Take(30.ToList()然后
var firstTen=temp.Take(10)
var nextTwenty=temp.Skip(10)。Take(20)如果您在使用所提供的答案时遇到问题,请在问题中解释您是如何使用这些答案的,以及它是如何解决您的问题的。@ManuelSchweigert这可能意味着问题写得不好。@ManuelSchweigert以什么方式解决不了您的问题?如果你愿意自己写,而不是使用别人提供的,那没关系,自己写,不要问别人怎么写。如果你觉得你的解决方案比所有现有的解决方案都好的话,你甚至可以把你的解决方案作为一个标准问题的答案发布出来。嗯,
source
应该是
publishedSource
?看起来像我需要的。谢谢@ManuelSchweigert如果在调用
Take
后实际上不需要继续计算查询,并且实际上可以重复整个查询直到该点继续获取项目,那么您应该相应地调整问题,否则,你会让人们寻找问题的答案,却找不到问题的答案。如果你只是想知道如何将查询结果缓存到数据源中,那么你应该问这个问题。@Servy我不需要你所说的任何东西。这个答案很适合我的问题…@Servy我想你误解了这个解决方案。这不会在序列上重复迭代,调用
Publish
可以防止这种情况发生。我首先尝试了类似的解决方案,然后再次尝试了您的代码以确保不会产生所需的结果:var ar=new[]{0,1,2,3,4};var it=ar.EnumerableOnce();它。采取(2);Console.WriteLine(it.First());//预期输出:2的输出为0。您必须记住,LINQ操作不会执行,除非您执行了某些操作;e、 g.
Take()
实际上不接受任何内容,它只是在管道中创建一个条件,用于在枚举结果后限制结果。如果不执行枚举,则什么也不会发生,因此需要
ToList()
DoTake()
。我确实认为一定是我…:DYou可以创建一个
EnumerabileCe
的变体,立即执行,例如
Take
Skip
,但LINQ不是这样设计的,我不建议这样做。@ManuelSchweigert在您的示例中,您没有得到2的预期结果,因为
it.Take(2)
没有被急切地评估,因此前两项永远不会被枚举。如果您将其更改为
it.Take(2).ToArray()
您将得到预期的答案。
System.Interactive
(我的解决方案)中的
Publish
方法的行为方式完全相同。
public void DoSkip(int n) {
    while (n > 0 && srcEnum.MoveNext())
    --n;
}

public void DoTake(int n) {
    while (n > 0 && srcEnum.MoveNext())
        --n;
}
var it1 = it.EnumerableOnce();
it1.DoTake(10);
var @continue = it1.Select(Mutate);
@continue.DoSkip(20);
@continue = @continue.Where(Filter);
@continue.DoTake(5);