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

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

Возможно ли создать функцию "ForceEvaluation", которая может определить, выполнил ли перечислимое событие ожидаемое выполнение, а затем вычислил перечислимый?

 public void Process(IEnumerable<Foo> foos)
 {
      IEnumerable<Foo> 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<Foo> foos)
 {
      if(??????)
      { return foos}
      else
      {return foos.ToList()}

 }

}

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

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

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

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

Ответ 1

Вы можете попробовать надежную проверку против IList<T> или ICollection<T>, но обратите внимание, что они все еще могут быть реализованы лениво - но это намного реже, а LINQ этого не делает - он просто использует итераторы (не ленивые коллекции). Итак:

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

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

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

Ответ 2

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

IEnumerable<t> deffered = someArray.Where(somecondition);

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

Ответ 3

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

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

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

IEnumerable<T> 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;
}

Ответ 4

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

public class ForceableEnumerable<T> : IEnumerable<T>
{
    IEnumerable<T> _enumerable;
    IEnumerator<T> _enumerator;

    public ForceableEnumerable(IEnumerable<T> enumerable)
    {
        _enumerable = enumerable;
    }

    public void ForceEvaluation()
    {
        if (_enumerator != null) {
            while (_enumerator.MoveNext()) {
            }
        }
    }

    #region IEnumerable<T> Members

    public IEnumerator<T> GetEnumerator()
    {
        _enumerator = _enumerable.GetEnumerator();
        return _enumerator;
    }

    #endregion

    #region IEnumerable Members

    System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }

    #endregion
}

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

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

EDIT:

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

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