Проблема: C# Foreach - прежде, после, даже, нечетный, Наконец, сначала

yield был разработан для C#2 (перед Linq в C#3).

Мы использовали его в большой степени в веб-приложении крупного предприятия C#2 при контакте с доступом к данным и в большой степени повторили вычисления.

Наборы являются большими любое время, у Вас есть несколько элементов, которые Вы собираетесь поразить многократно.

Однако в большом количестве сценариев доступа к данным у Вас есть большие количества элементов, которые необходимо не обязательно передать по кругу в большом большом наборе.

Это по существу, что эти SqlDataReader делает - это - вперед только пользовательский перечислитель.

, Чему yield позволяет, Вы сделать быстро, и с минимальным кодом пишут Ваши собственные перечислители.

Все yield делает мог быть сделан в C#1 - он просто взял стопки кода, чтобы сделать это.

Linq действительно максимизирует значение поведения урожая, но это, конечно, не единственное приложение.

9
задан Omar 19 October 2009 в 05:39
поделиться

7 ответов

LINQ ...

  • после: .SkipWhile (предикат) (оставлено неопределенным, поскольку ваше значение неясно)
  • до: .TakeWhile (предикат) (слева неопределенно поскольку ваш смысл не ясен)
  • последний: .Last ()
  • первый: .First ()
  • odd: .Where ((x, i) => i% 2 == 1)
  • четное: .Where ((x, i) => i% 2 == 0)
16
ответ дан 4 December 2019 в 08:33
поделиться

Джон Скит написал для этой цели SmartEnumerable . Его легко расширить, чтобы предоставить свойства IsOdd и IsEven .

Ответ Марка Гравелла хорош, потому что он прост, но он будет менее производительным в тех случаях, когда вы сначала хотите что-то сделать на все нечетные элементы, а затем все четные элементы, так как это потребовало бы перебора всех элементов дважды, в то время как только один раз строго необходим.

Я полагаю, вы могли бы написать функцию из элемента в какое-то перечисление и сгруппировать все элементы по значению перечисления, которому они сопоставляются. Тогда вы легко сможете работать с каждой группой по отдельности. Но я не уверен, как это будет работать, так как я не уверен, что конкретно делает группировка LINQ и насколько она отложена.

4
ответ дан 4 December 2019 в 08:33
поделиться
public class NavigationItem<T>
{
    readonly T _value;
    readonly bool _isFirst, _isLast, _isEven;

    internal NavigationItem(T value, bool isFirst, bool isLast, bool isEven)
    {
        _value = value;
        _isFirst = isFirst;
        _isLast = isLast;
        _isEven = isEven;
    }

    public T Value { get { return _value; } }
    public bool IsFirst { get { return _isFirst; } }
    public bool IsLast { get { return _isLast; } }
    public bool IsEven { get { return _isEven; } }
    public bool IsOdd { get { return !_isEven; } }
}

public static class CollectionNavigation
{
    public IEnumerable<NavigationItem<T>> ToNavigable<T>(this IEnumerable<T> collection)
    {
        if (collection == null) throw new ArgumentNullException("collection");
        return ToNavigableCore(collection);
    }

    private IEnumerable<NavigationItem<T>> ToNavigableCore<T>(IEnumerable<T> collection)
    {
        using(var enumerator = collection.GetEnumerator())
        {
            if (enumerator.MoveNext())
            {
                T current = enumerator.Current;
                bool isFirst = true, isEven = true;

                while(enumerator.MoveNext())
                {
                    yield return new NavigationItem<T>(current, isFirst, false, isEven);
                    isFirst = false;
                    isEven = !isEven;
                    current = enumerator.Current;
                }

                yield return new NavigationItem<T>(current, isFirst, true, isEven);
            }
        }
    }
}

Использование:

foreach(var item in collection.ToNavigable())
{
    if (item.IsFirst) { ... }
    if (item.IsLast) { ... }
    if (item.IsEven) { ... }
    if (item.IsOdd) { ... }

    Console.WriteLine(item.Value);
}
4
ответ дан 4 December 2019 в 08:33
поделиться

Конечно, он должен быть общим, чтобы вам не приходилось разыгрывать ценности. Вы можете использовать делегат Action во фреймворке вместо объявления своего собственного.

public enum ForEachExecuction {
  Concurrent,
  Seperate
}

public static class ForEach<T> {

  private static bool Call(Action<T> function, bool condition, T value, Action<T> concurrent) {
    condition &= function != null;
    if (condition) {
      function(value);
      if (concurrent != null) concurrent(value);
    }
    return condition;
  }

  public static void Loop(
    IList<T> collection,
    Action<T> function,
    Action<T> before,
    Action<T> first,
    Action<T> evens,
    Action<T> odds,
    Action<T> last,
    Action<T> after,
    ForEachExecuction when)
  {
    Action<T> concurrent = when == ForEachExecuction.Concurrent?function:null;
    for (int i = 0; i < collection.Count; i++) {
      T value = collection[i];
      Call(before, i == 0, value, null);
      if (!Call(first, i == 0, value, concurrent)) {
        if (!Call(evens, i % 2 != 0, value, concurrent)) {
          if (!Call(odds, i % 2 == 0, value, concurrent)) {
            if (!Call(last, i==collection.Count-1, value, concurrent)) {
              function(value);
            }
          }
        }
      }
      Call(after, i == collection.Count - 1, value, null);
    }
  }

}

Вызов:

string[] testCollection = { "1", "2", "3", "4", "5" };

Action<string> primaryFunction = delegate(string s) { Console.WriteLine(s); }
Action<string> first = delegate(string s) { Console.WriteLine("first"); }
Action<string> odd = delegate(string s) { Console.WriteLine("odd"); }
Action<string> after = delegate(string s) { Console.WriteLine("after"); }

ForEach<string>.Loop(testCollection, primaryFunction, null, first, null, odd, null, after, ForEachExecuction.Concurrent);

Или, возможно:

ForEach<string>.Loop(testCollection,
  (s) => Console.WriteLine(s),
  null,
  (s) => Console.WriteLine("first"),
  null,
  (s) => Console.WriteLine("odd"),
  null,
  (s) => Console.WriteLine("after"),
  ForEachExecuction.Concurrent
);
1
ответ дан 4 December 2019 в 08:33
поделиться
for (;;) {}

Серьезно.

* PS: Условия вроде Odd и After зависят от строгого и значимого порядка, что делает foreach неправильным как по значению, так и по синтаксису. Нужно знать, когда перестать следовать моде. Обратите внимание, как предлагаемый LINQ .SkipWhile (предикат) семантически отличается от After (index) *

0
ответ дан 4 December 2019 в 08:33
поделиться

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

Лучше предоставить все эти возможности в виде отдельных компонентов, которые вы можете составить для каждой ситуации.

Linq предоставляет отличную отправную точку. Подумайте о foreach как о способе преобразования списка объектов в список действий. Воспринимайте это буквально: последовательность делегатов Action или IEnumerable .

Для удобного выполнения последовательности действий вам необходимо:

public static void Execute(this IEnumerable<Action> actions)
{
    foreach (var a in actions)
        a();
}

Теперь проблема проста чтобы обеспечить управление последовательностью (большая часть которой уже находится в Linq), чтобы вы могли составить список действий в соответствии с вашими требованиями, чтобы вы могли поставить Execute в конце.

0
ответ дан 4 December 2019 в 08:33
поделиться

My Code, not elegant.

    public enum forEachExeuction
    {
        Concurrent,
        Seperate
    }

    public delegate void forEachDelegate(object o);

    public static void forEach(
        IList collection,
        forEachDelegate function,
        forEachDelegate before,
        forEachDelegate first,
        forEachDelegate evens,
        forEachDelegate odds,
        forEachDelegate last,
        forEachDelegate after,
        forEachExeuction when)
    {
        bool doBefore = before != null;
        bool doFirst = first != null;
        bool doEvens = evens != null;
        bool doOdds = odds != null;
        bool doLast = last != null;
        bool doAfter = after != null;
        bool conCurrent = when == forEachExeuction.Concurrent;

        int collectionCount = collection.Count;

        for (int i = 0; i < collectionCount; i++)
        {
            if (doBefore && i == 0)
            {
                before(collection[i]);
            }

            if (doFirst && i == 0)
            {
                first(collection[i]);
                if (conCurrent)
                    function(collection[i]);
            }
            else if (doEvens && i % 2 != 0)
            {
                evens(collection[i]);
                if (conCurrent)
                    function(collection[i]);
            }
            else if (doOdds && i % 2 == 0)
            {
                odds(collection[i]);
                if (conCurrent)
                    function(collection[i]);
            }
            else if (doLast && i == collectionCount - 1)
            {
                last(collection[i]);
                if (conCurrent)
                    function(collection[i]);
            }
            else
            {
                function(collection[i]);
            }

            if (after != null && i == collectionCount - 1)
            {
                after(collection[i]);
            }
        }
    }

Simple call to it:

    string[] testCollection = {"1", "2", "3", "4", "5"};

    forEachDelegate primaryFunction = delegate(object o)
    {
        Response.Write(o.ToString());
    };

    forEachDelegate first = delegate(object o)
    {
        Response.Write("first");
    };

    forEachDelegate odd = delegate(object o)
    {
        Response.Write("odd");
    };

    forEachDelegate after = delegate(object o)
    {
        Response.Write("after");
    };

    Randoms.forEach(testCollection, primaryFunction, null, first, null, odd, null, after, Randoms.forEachExeuction.Concurrent);

You would need to cast each object in each delegate to the proper object. Default parameters and named parameters would make this look much better. /wait for C# 4.0

0
ответ дан 4 December 2019 в 08:33
поделиться
Другие вопросы по тегам:

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