C # и linq в циклах [дубликат]

Очистить и перестроить

. «Чистая» сборки может удалить «мертвую древесину», которая может быть оставлена ​​лежащей рядом с предыдущими сборками, неудачными сборками, неполными сборками и другими проблемами сборки.

В общем случае среда IDE или сборка будет включать в себя некоторую форму «чистой» функции, но это может быть неправильно настроено (например, в ручном файле) или может завершиться неудачей (например, промежуточные или результирующие двоичные файлы - только).

После завершения «очистки» убедитесь, что «чистый» преуспел, и весь сгенерированный промежуточный файл (например, автоматический файл makefile) был успешно удален.

Этот процесс можно рассматривать как конечный вариант, но часто является хорошим первым шагом ; особенно если недавно был добавлен код, связанный с ошибкой (локально или из исходного репозитория).

35
задан Wasabi 8 November 2013 в 21:27
поделиться

5 ответов

Я просто высмеивал ваш код с помощью этого

foreach(var v in Enumerable.Range(1,10).Skip(1))
    v.Dump();

И здесь генерируется IL.

IL_0001:  nop         
IL_0002:  ldc.i4.1    
IL_0003:  ldc.i4.s    0A 
IL_0005:  call        System.Linq.Enumerable.Range
IL_000A:  ldc.i4.1    
IL_000B:  call        System.Linq.Enumerable.Skip//Call to Skip
IL_0010:  callvirt    System.Collections.Generic.IEnumerable<System.Int32>.GetEnumerator
IL_0015:  stloc.1     // CS$5$0000
IL_0016:  br.s        IL_0026
IL_0018:  ldloc.1     // CS$5$0000
IL_0019:  callvirt    System.Collections.Generic.IEnumerator<System.Int32>.get_Current
IL_001E:  stloc.0     // v
IL_001F:  ldloc.0     // v
IL_0020:  call        LINQPad.Extensions.Dump
IL_0025:  pop         
IL_0026:  ldloc.1     // CS$5$0000
IL_0027:  callvirt    System.Collections.IEnumerator.MoveNext
IL_002C:  stloc.2     // CS$4$0001
IL_002D:  ldloc.2     // CS$4$0001
IL_002E:  brtrue.s    IL_0018
IL_0030:  leave.s     IL_0042
IL_0032:  ldloc.1     // CS$5$0000
IL_0033:  ldnull      
IL_0034:  ceq         
IL_0036:  stloc.2     // CS$4$0001
IL_0037:  ldloc.2     // CS$4$0001
IL_0038:  brtrue.s    IL_0041
IL_003A:  ldloc.1     // CS$5$0000
IL_003B:  callvirt    System.IDisposable.Dispose
IL_0040:  nop         
IL_0041:  endfinally  

Как вы можете видеть, Skip вызывается только один раз.

Эквивалентный код c # будет выглядеть примерно так:

IEnumerator<int> e = ((IEnumerable<int>)values).GetEnumerator();//Get the enumerator
try
{
  int m;//This variable is here prior to c#5.0
  while(e.MoveNext())
  {//int m; is declared here starting from c#5.0
    m = (int)(int)e.Current;
    //Your code here
  }
}
finally
{
  if (e != null) ((IDisposable)e).Dispose();
}

. Рассмотрим приведенный ниже код. Если для каждой итерации используются следующие вызовы VeryLongRunningMethodThatReturnsEnumerable, то это будет кошмар. Огромная ошибка в дизайне языка. К счастью, этого не делает.

foreach(var obj in VeryLongRunningMethodThatReturnsEnumerable())
{
   //Do something with that obj
}
39
ответ дан Sriram Sakthivel 28 August 2018 в 04:18
поделиться

Какова ваша итерация - результат команды:

myCollection.Skip(1)

Это эффективно возвращает IEnumerable типа myCollection, который пропустил первый элемент. Поэтому ваш foreach тогда против нового IEnumerable, которому не хватает первого элемента. foreach заставляет фактическую оценку полученного метода Skip(int) посредством перечисления (его выполнение откладывается до перечисления, как и другие методы LINQ, такие как Where и т. Д.). Это будет то же самое, что:

var mySkippedCollection = myCollection.Skip(1);
foreach (object i in mySkippedCollection)
...

Вот код, который Skip(int) действительно заканчивает выполнение:

private static IEnumerable<TSource> SkipIterator<TSource>(IEnumerable<TSource> source, int count)
{
    using (IEnumerator<TSource> enumerator = source.GetEnumerator())
    {
        while (count > 0 && enumerator.MoveNext())
        {
            count--;
        }
        if (count <= 0)
        {
            while (enumerator.MoveNext())
            {
                yield return enumerator.Current; // <-- here's your lazy eval
            }
        }
    }
    yield break;
}
2
ответ дан Haney 28 August 2018 в 04:18
поделиться

Извлеките его, и он, вероятно, станет более ясным.

var myCollection = new List<object>();
var skipped = myCollection.Skip(1);

foreach (var i in skipped) {
    Console.WriteLine(i.ToString());
}

Так что пропущено только IEnumerable, которое теперь перечисляет foreach.

Вот что выглядит IL как и в этом случае:

IL_0000:  newobj      System.Collections.Generic.List<System.Object>..ctor
IL_0005:  stloc.0     // myCollection
IL_0006:  ldloc.0     // myCollection
IL_0007:  ldc.i4.1    
IL_0008:  call        System.Linq.Enumerable.Skip
IL_000D:  stloc.1     // skipped
IL_000E:  ldloc.1     // skipped
IL_000F:  callvirt    System.Collections.Generic.IEnumerable<System.Object>.GetEnumerator
IL_0014:  stloc.3     // CS$5$0000
IL_0015:  br.s        IL_0029
IL_0017:  ldloc.3     // CS$5$0000
IL_0018:  callvirt    System.Collections.Generic.IEnumerator<System.Object>.get_Current
IL_001D:  stloc.2     // i
IL_001E:  ldloc.2     // i
IL_001F:  callvirt    System.Object.ToString
IL_0024:  call        System.Console.WriteLine
IL_0029:  ldloc.3     // CS$5$0000
IL_002A:  callvirt    System.Collections.IEnumerator.MoveNext
IL_002F:  brtrue.s    IL_0017
IL_0031:  leave.s     IL_003D
IL_0033:  ldloc.3     // CS$5$0000
IL_0034:  brfalse.s   IL_003C
IL_0036:  ldloc.3     // CS$5$0000
IL_0037:  callvirt    System.IDisposable.Dispose
IL_003C:  endfinally  

IL для вашего кода выглядит схоже:

var myCollection = new List<object>();

foreach (var i in myCollection.Skip(1)) {
    Console.WriteLine(i.ToString());
}

IL_0000:  newobj      System.Collections.Generic.List<System.Object>..ctor
IL_0005:  stloc.0     // myCollection
IL_0006:  ldloc.0     // myCollection
IL_0007:  ldc.i4.1    
IL_0008:  call        System.Linq.Enumerable.Skip <-- 1 Call to .Skip() outside the loop.
IL_000D:  callvirt    System.Collections.Generic.IEnumerable<System.Object>.GetEnumerator
IL_0012:  stloc.2     // CS$5$0000
IL_0013:  br.s        IL_0027
IL_0015:  ldloc.2     // CS$5$0000
IL_0016:  callvirt    System.Collections.Generic.IEnumerator<System.Object>.get_Current
IL_001B:  stloc.1     // i
IL_001C:  ldloc.1     // i
IL_001D:  callvirt    System.Object.ToString
IL_0022:  call        System.Console.WriteLine
IL_0027:  ldloc.2     // CS$5$0000
IL_0028:  callvirt    System.Collections.IEnumerator.MoveNext
IL_002D:  brtrue.s    IL_0015
IL_002F:  leave.s     IL_003B
IL_0031:  ldloc.2     // CS$5$0000
IL_0032:  brfalse.s   IL_003A
IL_0034:  ldloc.2     // CS$5$0000
IL_0035:  callvirt    System.IDisposable.Dispose
IL_003A:  endfinally  

У него все еще есть один вызов .Skip ().

6
ответ дан Jason Punyon 28 August 2018 в 04:18
поделиться

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

4
ответ дан Ondrej Janacek 28 August 2018 в 04:18
поделиться

Вы должны понимать, как работает foreach. Этот цикл foreach:

foreach(T t in GetSomeEnumerable())
    DoSomethingWithT(t);

эквивалентен этому коду:

var e = GetSomeEnumerable().GetEnumerator();
try{
    while(e.MoveNext()){
        T t = (T)e.Current; // unless e is the generic IEnumerator<T>,
                            // in which case, there is no cast
        DoSomethingWithT(t);
    }
}finally{
    if(e is IDisposable)
        e.Dispose();
}
24
ответ дан P Daddy 28 August 2018 в 04:18
поделиться
Другие вопросы по тегам:

Похожие вопросы: