代码之家  ›  专栏  ›  技术社区  ›  Steve Guidi

是否可以克隆IEnumerable<T>实例,保存迭代状态的副本?

  •  9
  • Steve Guidi  · 技术社区  · 16 年前

    IEnumerator<T> 这样我就可以从集合中的特定位置重新启动枚举过程。显然,这样做对实现 IList

    有没有一个聪明的方法来完成这项任务使用的组合 yield 语句和Linq函数?我找不到合适的工作 Clone() Enumerable.Skip() 将新枚举数重新定位到所需的恢复点。

    另外,我希望使解决方案尽可能通用,而不必依赖于任何具体集合的状态。

    6 回复  |  直到 16 年前
        1
  •  5
  •   Steve Guidi    16 年前

    Queue<T> )从一个迭代器而不是另一个迭代器中消耗的数据的数量(如果将一个迭代器提升1百万个位置,而不使用另一个迭代器,则会变得混乱/昂贵)。我 但是,你认为重新思考设计,仅仅使用 GetEnumerator() foreach )重新开始-或将数据(如果短)缓冲在列表/数组/任何内容中。


    PushLINQ "; 它不是克隆迭代器,而是允许多个“东西”进行复制 消费 同时 .

    在本例中(摘自Jon的页面),我们并行计算多个聚合:

    // Create the data source to watch
    DataProducer<Voter> voters = new DataProducer<Voter>();
    
    // Add the aggregators
    IFuture<int> total = voters.Count();
    IFuture<int> adults = voters.Count(voter => voter.Age >= 18);
    IFuture<int> children = voters.Where(voter => voter.Age < 18).Count();
    IFuture<int> youngest = voters.Min(voter => voter.Age);
    IFuture<int> oldest = voters.Select(voter => voter.Age).Max();
    
    // Push all the data through
    voters.ProduceAndEnd(Voter.AllVoters());
    
    // Write out the results
    Console.WriteLine("Total voters: {0}", total.Value);
    Console.WriteLine("Adult voters: {0}", adults.Value);
    Console.WriteLine("Child voters: {0}", children.Value);
    Console.WriteLine("Youngest vote age: {0}", youngest.Value);
    Console.WriteLine("Oldest voter age: {0}", oldest.Value);
    
        2
  •  3
  •   supercat    16 年前

    给定一个iEnumerable,可能会生成两个或多个iEnumerable,每个iEnumerable的行为都类似于其原始版本或克隆版本。对于“最远的”一个的MoveNext请求将从原始iEnumerable读取新数据,并为其他iEnumerable缓冲。但是,除非最初的iEnumerable支持这样的“钩子”功能,否则我认为没有任何方法可以在数据进入时锁定它。

        3
  •  2
  •   JerKimball    16 年前

    能够 做这样的事……;)

    class Program
    {
        static void Main(string[] args)
        {
            var bar = new Program().Foo();
    
            // Get a hook to the underlying compiler generated class
            var barType = bar.GetType().UnderlyingSystemType;
            var barCtor = barType.GetConstructor(new Type[] {typeof (Int32)});
            var res = barCtor.Invoke(new object[] {-2}) as IEnumerable<int>;
    
            // Get our enumerator
            var resEnum = res.GetEnumerator();
            resEnum.MoveNext();
            resEnum.MoveNext();
            Debug.Assert(resEnum.Current == 1);
    
            // Extract and save our state
            var nonPublicMap = new Dictionary<FieldInfo, object>();
            var publicMap = new Dictionary<FieldInfo, object>();
            var nonpublicfields = resEnum.GetType().GetFields(BindingFlags.NonPublic | BindingFlags.Instance);
            var publicfields = resEnum.GetType().GetFields(BindingFlags.Public | BindingFlags.Instance);
            foreach(var field in nonpublicfields)
            {
                var value = field.GetValue(resEnum);
                nonPublicMap[field] = value;
            }
            foreach (var field in publicfields)
            {
                var value = field.GetValue(resEnum);
                publicMap[field] = value;                
            }
    
            // Move about
            resEnum.MoveNext();
            resEnum.MoveNext();
            resEnum.MoveNext();
            resEnum.MoveNext();
            Debug.Assert(resEnum.Current == 5);
    
            // Restore state            
            foreach (var kvp in nonPublicMap)
            {
                kvp.Key.SetValue(resEnum, kvp.Value);
            }
            foreach (var kvp in publicMap)
            {
                kvp.Key.SetValue(resEnum, kvp.Value);                
            }
    
            // Move about
            resEnum.MoveNext();
            resEnum.MoveNext();
            Debug.Assert(resEnum.Current == 3);
        }
    
        public IEnumerable<int> Foo()
        {
            for (int i = 0; i < 10; i++)
            {
                yield return i;
            }
            yield break;
        }
    
    }
    
        4
  •  1
  •   ICR    16 年前

    如果是后者,下面的方法可能会起作用:

    public class SaveableEnumerable<T> : IEnumerable<T>, IDisposable
    {
        public class SaveableEnumerator : IEnumerator<T>
        {
            private IEnumerator<T> enumerator;
    
            internal SaveableEnumerator(IEnumerator<T> enumerator)
            {
                this.enumerator = enumerator;
            }
    
            public void Dispose() { }
    
            internal void ActuallyDispose()
            {
                enumerator.Dispose();
            }
    
            public bool MoveNext()
            {
                return enumerator.MoveNext();
            }
    
            public void Reset()
            {
                enumerator.Reset();
            }
    
            public T Current
            {
                get { return enumerator.Current; }
            }
    
            object IEnumerator.Current
            {
                get { return enumerator.Current; }
            }
        }
    
        private SaveableEnumerator enumerator;
    
        public SaveableEnumerable(IEnumerable<T> enumerable)
        {
            this.enumerator = new SaveableEnumerator(enumerable.GetEnumerator());
        }
    
        public IEnumerator<T> GetEnumerator()
        {
            return enumerator;
        }
    
        IEnumerator IEnumerable.GetEnumerator()
        {
            return enumerator;
        }
    
        public void Dispose()
        {
            enumerator.ActuallyDispose();
        }
    }
    

    现在您可以执行以下操作:

    using (IEnumerable<int> counter = new SaveableEnumerable<int>(CountableEnumerable()))
    {
        foreach (int i in counter)
        {
            Console.WriteLine(i);
            if (i > 10)
            {
                break;
            }
        }
        DoSomeStuff();
        foreach (int i in counter)
        {
            Console.WriteLine(i);
            if (i > 20)
            {
                break;
            }
        }
    }
    
        5
  •  1
  •   BenMaddox    16 年前

    杰金鲍尔的方法很有趣。我试着把它带到下一个层次。这将使用反射创建新实例,然后在新实例上设置值。我还发现C#Depth中的这一章非常有用。 Iterator block implementation details: auto-generated state machines

    static void Main()
    {
        var counter = new CountingClass();
        var firstIterator = counter.CountingEnumerator();
        Console.WriteLine("First list");
        firstIterator.MoveNext();
        Console.WriteLine(firstIterator.Current);
    
        Console.WriteLine("First list cloned");
        var secondIterator = EnumeratorCloner.Clone(firstIterator);
    
        Console.WriteLine("Second list");
        secondIterator.MoveNext();
        Console.WriteLine(secondIterator.Current);
        secondIterator.MoveNext();
        Console.WriteLine(secondIterator.Current);
        secondIterator.MoveNext();
        Console.WriteLine(secondIterator.Current);
    
        Console.WriteLine("First list");
        firstIterator.MoveNext();
        Console.WriteLine(firstIterator.Current);
        firstIterator.MoveNext();
        Console.WriteLine(firstIterator.Current);
    }
    
    public class CountingClass
    {
        public IEnumerator<int> CountingEnumerator()
        {
            int i = 1;
            while (true)
            {
                yield return i;
                i++;
            }
        }
    }
    
    public static class EnumeratorCloner
    {
        public static T Clone<T>(T source) where T : class, IEnumerator
        {
            var sourceType = source.GetType().UnderlyingSystemType;
            var sourceTypeConstructor = sourceType.GetConstructor(new Type[] { typeof(Int32) });
            var newInstance = sourceTypeConstructor.Invoke(new object[] { -2 }) as T;
    
            var nonPublicFields = source.GetType().GetFields(BindingFlags.NonPublic | BindingFlags.Instance);
            var publicFields = source.GetType().GetFields(BindingFlags.Public | BindingFlags.Instance);
            foreach (var field in nonPublicFields)
            {
                var value = field.GetValue(source);
                field.SetValue(newInstance, value);
            }
            foreach (var field in publicFields)
            {
                var value = field.GetValue(source);
                field.SetValue(newInstance, value);
            }
            return newInstance;
        }
    }
    
        6
  •  0
  •   kyoryu    16 年前

    那你呢 真正地 我希望以后能够恢复迭代,对吗?克隆枚举器或集合是你认为你会怎么做的?

    这将为每个“正在运行”的枚举数创建一个额外的IEnumerable副本,但我认为它可以满足您的需要。