Можно ли определить, что IEnumerable отклонил выполнение ожидания?

У меня есть функция, которая принимает Enumerable. Мне нужно убедиться, что счетчик оценен, но я не хочу его копировать (например, через ToList () или ToArray ()), если он готов в списке или какой-либо другой «замороженной» коллекции. В Frozen я имею в виду коллекции, в которых набор элементов уже установлен, например List, Array, FsharpSet, Collection и т. Д., В отличие от таких элементов linq, как Select () и where ().

Можно ли создать функцию «ForceEvaluation», которая может определить, не перечислил ли перечислимое значение ожидаемое выполнение, а затем вычислить перечислимое?

public void Process(IEnumerable foos) { IEnumerable evalutedFoos = ForceEvaluation(foos) EnterLockedMode(); // all the deferred processing needs to have been done before this line. foreach (Foo foo in foos) { Bar(foo); } } public IEnumerable ForceEvaluation(IEnumerable foos) { if(??????) { return foos} else {return foos.ToList()} } 

}

После нескольких исследований я понял, что это практически невозможно в любом практическом смысле и потребует комплексного проверки кода каждого iteratorа.

Поэтому я собираюсь перейти к варианту ответа Марка и создать белый список известных безопасных типов и просто вызвать ToList () что-то не на том, что не в белом списке.

Спасибо за вашу помощь.

Edit * После еще большего отражения я понял, что это эквивалентно проблеме остановки. Это очень невозможно.

    Вы можете попробовать надежную проверку против IList или ICollection , но обратите внимание, что они все еще могут быть реализованы лениво – но это намного реже, и LINQ этого не делает – он просто использует iteratorы (не ленивые коллекции) , Так:

     var list = foos as IList; if(list != null) return list; // unchanged return foos.ToList(); 

    Обратите внимание, что это отличается от обычного .ToList() , который каждый раз возвращает вам другой список, чтобы гарантировать, что ничего неожиданного не произойдет.

    Большинство типов конкретных сборок (включая T[] и List ) удовлетворяют IList . Я не знаком с коллекциями F # – вам нужно это проверить.

    Что-то, что сработало для меня:

     IEnumerable deffered = someArray.Where(somecondition); if (deffered.GetType().UnderlyingSystemType.Namespace.Equals("System.Linq")) { //this is a deffered executin IEnumerable } 

    Я бы избегал этого, если вы хотите убедиться, что он «заморожен». Оба элемента Array и List <> могут быть изменены в любое время (например, позорная «коллекция, измененная во время итерации»). Если вам действительно нужно убедиться, что IEnumerable оценивается и не изменяется под кодом, чем копировать все элементы в свой собственный список / массив.

    Могут быть другие причины попробовать – то есть некоторые операции во время выполнения делают специальные проверки для коллекции, являющейся массивом для их оптимизации. Или у вас есть специальная версия для специализированного интерфейса, такого как ICollection или IQueryable, в дополнение к универсальному IEnumerable.

    EDIT: пример изменения коллекции во время итерации:

     IEnumerable collectionAsEnumrable = collection; foreach(var i in collectionAsEnumrable) { // something like following can be indirectly called by // synchronous method on the same thread collection.Add(i.Clone()); collection[3] = 33; } 

    Если в вашем случае можно использовать обертку, вы можете сделать что-то вроде этого

     public class ForceableEnumerable : IEnumerable { IEnumerable _enumerable; IEnumerator _enumerator; public ForceableEnumerable(IEnumerable enumerable) { _enumerable = enumerable; } public void ForceEvaluation() { if (_enumerator != null) { while (_enumerator.MoveNext()) { } } } #region IEnumerable Members public IEnumerator GetEnumerator() { _enumerator = _enumerable.GetEnumerator(); return _enumerator; } #endregion #region IEnumerable Members System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { return GetEnumerator(); } #endregion } 

    Или реализовать такой метод силы, если вы хотите оценить в любом случае

     public void ForceEvaluation() { if (_enumerator == null) { _enumerator = _enumerable.GetEnumerator(); } while (_enumerator.MoveNext()) { } } 

    РЕДАКТИРОВАТЬ:

    Если вы хотите, чтобы перечисление оценивалось только один раз в любом случае, вы можете изменить GetEnumerator на

     public IEnumerator GetEnumerator() { if (_enumerator == null) } _enumerator = _enumerable.GetEnumerator(); } return _enumerator; }