是否可以在foreach循环中递增标识符而不评估其表达式?

时间:2010-07-23 01:01:06

标签: c#

我有一系列对象,我想迭代以最终识别某种类型的模式。目前我有一堆if语句检查表达式的标志。例如,我有if(!foundFirstObjectOfTypeA)的内容。一旦我找到了类型A的第一个对象,我就会将该标志设置为true,我们将不再执行该块并继续执行下一个块。我有几种类型的模式可以识别,因此它创建了一大块难以理解和丑陋的if语句。

有没有办法在没有评估foreach循环中的表达式的情况下查看/查看标识符?如果我可以按照if(identifer == ObjectA&& identifer.next == ObjectB)的方式做一些事情,那么它将使我的代码更具可读性和可理解性,而且我可以在不必设置标志的情况下进行处理。

如果没有直接的方法/方式,有人会有一些聪明的解决方法来模拟我想要的效果吗?

5 个答案:

答案 0 :(得分:6)

使用for循环来增加i,然后只需查看object[i]object[i+1]。你使用什么类型的收藏?一个列表?数组?所有foreach循环都会隐藏计数器。如果我了解您的情况,那么使用foreach循环而不是for循环绝对是没有的理由。

这适用于任何列表和任何对象序列:

<强>功能:

public static bool ContainsSequence
    (System.Collections.Generic.IList<object> list, params object[] seq)
{
    for (int i = 0; i < list.Count() - seq.Count() + 1; i++)
    {
        int j;
        for (j = 0; j < seq.Count(); j++)
        {
            if (list[i + j] != seq[j])
                break;
        }
        if (j == seq.Count())
            return true;
    }
    return false;
}

<强>用法:

private static void Main(string[] args)
{
    var A = new object();
    var B = new object();
    var C = new object();
    var D = new object();

    var list = new[] {A, B, C, D};
    Console.WriteLine(ContainsSequence(list, B, C, D));
    Console.WriteLine(ContainsSequence(list, A, D, C, B, A, C));
    Console.WriteLine(ContainsSequence(list, A, B));
}

<强>输出:

True
False
True

如果您知道要查找的对象数量(如果您只查找两个,它几乎是 Hamid Nazari的答案 - 可以使函数变得更简单 - 您只需需要在下面添加 AndrewS 的绑定检查。)

如果你想要序列开头的 index ,你可以将我的函数的返回类型更改为int并返回i而不是true(和返回-1而不是false。)

答案 1 :(得分:2)

我认为没有直接的支持,但你可以随时效仿。

例如(解决方案的一部分):

  static IEnumerable<Peekable<T>> ToPeekable(this IEnumerable<T> stream)
  {
     T prev = <get first item, and move...>
     foreach(T item in stream)
     {
       yield return new Peekable<T>(prev, item);
       prev = item;
    }
    yield return new Peekable<T>(item, default(T));
  }

更新:缺少部分

class Peekable<T>
{
    public Peekable(T current, T next)
    {
        Current = current;
        Next = next;
    }

    public T Current { get; private set; }
    public T Next { get; private set; }
}

static class PeekableAdaptor
{
    public static IEnumerable<Peekable<T>> ToPeekable<T>(this IEnumerable<T> stream)
    {
        var source = stream.GetEnumerator();

        if (!source.MoveNext()) 
            yield break;

        T prev = source.Current;

        while (source.MoveNext())
        {
            yield return new Peekable<T>(prev, source.Current);
            prev = source.Current;
        }

        yield return new Peekable<T>(prev, default(T));
    }
}

class Program
{
    static void Main(string[] args)
    {
        foreach (var pair in Enumerable.Range(1, 10).ToPeekable())
        {
            Console.WriteLine(pair.Current, pair.Next);
        }
    }
}

答案 2 :(得分:0)

如果你使用for循环,你可以这样做。

for (int i = 0; i < list.Count - 1; i++)
    if (list[i] == ObjectA && list[i + 1] == ObjectB)
        ; // Your code

答案 3 :(得分:0)

IEnumerable是一个非常轻的forward-only迭代器实现。如果您查看IEnumerator interface,则只有三个成员:

作为解决方案,您可以手动迭代IEnumerable或使用循环。

bool MoveNext()
void Reset()
object Current

因此,唯一的操作是前进,重置和检索当前项目。没有内置的能力来偷看接下来会发生什么。

IEnumerable version

bool someFlag = false;
IEnumerable<object> collection = new object[]{"string", true, 4, DateTime.Now};
using (var enumerator = collection.GetEnumerator()) {
  while (true ) {
   bool end = enumerator.MoveNext();
   if (!end) {
      if (enumerator.Current.GetType() == typeof(string)) {
        end = enumerator.MoveNext();
        if (!end && enumerator.Current.GetType() == typeof(bool))
          someFlag = true;
      }
   } 
   if (end) break;
  }
}

循环版

bool someFlag = false;
List<object> collection = new object[]{"string", true, 4, DateTime.Now};
int i = 0;
while (true ) {
   bool end = i < collection.Count;
   if (!end) {
      if (collection[i] == typeof(string)) {
        i++;
        end = i < collection.Count;
        if (!end && collection[i] == typeof(bool))
          someFlag = true;
      }
   } 
   if (end) break;
  }
}

答案 4 :(得分:0)

使用我对此question的回答,我想出了这种变体作为可能的解决方案。这适用于任何类型的集合;即使是那些无法编入索引的人。

foreach (var item in ForEachHelper.Wrap(collection))
{
    Console.WriteLine("Position=" + item.Index.ToString());
    Console.WriteLine("Current=" + item.Current.ToString());
    if (item.HasNext)
    {
        Console.WriteLine("Next=" + item.Next.ToString());
    }
}

以下是相关代码。

public static class ForEachHelper
{
    public sealed class Item<T>
    {
        public int Index { get; set; }
        public T Current { get; set; }
        public T Next { get; set; }
        public bool HasNext { get; set; }
    }

    public static IEnumerable<Item<T>> Wrap<T>(IEnumerable<T> enumerable)
    {
        IEnumerator<T> enumerator = enumerable.GetEnumerator();
        try
        {
            var item = new Item<T>();
            item.Index = 0;
            item.Current = default(T);
            item.Next = default(T);
            item.HasNext = false;
            if (enumerator.MoveNext())
            {
                item.Current = enumerator.Current;
                if (enumerator.MoveNext())
                {
                    item.Next = enumerator.Current;
                    item.HasNext = true;
                }
                while (item.HasNext)
                {
                    var next = new Item<T>();
                    next.Index = item.Index + 1;
                    next.Current = item.Next;
                    next.Next = default(T);
                    if (enumerator.MoveNext())
                    {
                        next.Next = enumerator.Current;
                        next.HasNext = true;
                    }
                    var current = item;
                    item = next;
                    yield return current;
                }
                yield return item;
            }
        }
        finally
        {
            enumerator.Dispose();
        }
    }
}