Можно ли определить, что 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;
}