你可以在C#中乱序列举一个集合吗?

时间:2008-10-31 19:07:08

标签: c# loops foreach

有没有办法使用foreach循环向后或以完全随机的顺序迭代集合?

11 个答案:

答案 0 :(得分:15)

使用System.Linq你可以......

// List<...> list;
foreach (var i in list.Reverse())
{
}

对于随机顺序,您必须使用list.OrderBy(另一个Linq扩展名)随机排序,然后迭代该有序列表。

var rnd = new Random();
var randomlyOrdered = list.OrderBy(i => rnd.Next());
foreach (var i in randomlyOrdered)
{
}

答案 1 :(得分:15)

正如其他答案所提到的,Reverse() extension method将允许您以相反的顺序枚举序列。

这是一个随机枚举扩展方法:

public static IEnumerable<T> OrderRandomly<T>(this IEnumerable<T> sequence)
{
    Random random = new Random();
    List<T> copy = sequence.ToList();

    while (copy.Count > 0)
    {
        int index = random.Next(copy.Count);
        yield return copy[index];
        copy.RemoveAt(index);
    }
}

您的用法是:

foreach (int n in Enumerable.Range(1, 10).OrderRandomly())
    Console.WriteLine(n);

答案 2 :(得分:1)

我认为没有办法直接这样做,但使用通过as good关键字返回新集合的扩展方法几乎yield returnReverse方法,OrderBy之类的方法也可以。

示例:如果您使用Reverse()上的LINQ扩展方法IEnumerable<T>yield return使用foreach(var myThing in myCollection.Reverse())以相反的顺序提供集合,那么执行yield return将枚举该集合的顺序相反。

重要:{{1}}是关键。它的意思是“当我枚举这个集合时,然后去取东西。”与仅构建 new 的替代方案相反,反向收集,这是非常低效并且可能具有副作用。

答案 3 :(得分:1)

从C#2.0开始,您可以使用yield关键字来实现自定义迭代器。您可以在MSDN http://msdn.microsoft.com/en-us/library/9k7k7cf0.aspx

上详细了解yield关键字

您可以将yield视为从循环内部返回值的能力,但是您应该参考上面的链接以获得它们是什么以及它们可以做什么的完整解释。

我写了一个关于如何实现几个自定义迭代器的简短示例。我已经将它们实现为扩展方法(http://msdn.microsoft.com/en-us/library/bb383977.aspx)以使代码更加流式化,我还使用数组初始化器(http://msdn.microsoft.com/en-us/library/aa664573.aspx)来设置整数列表的初始值。

扩展方法和数组初始化器都不是实现自定义迭代器所必需的,但它们是c#3.0的很好的功能,它有助于编写更清晰的代码

以下是我的例子。它显示了如何通过仅返回奇数,偶数,反转或完全随机的数字来迭代整数列表。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            List<int> ints = 
                new List<int> { 1,2,3,4,5,6,7,8,9,10};

            Console.WriteLine("Iterating over Odd numbers only.");
            foreach (int i in ints.Odd())
            {
                Console.WriteLine(i);
            }

            Console.WriteLine("Iterating over Even numbers only.");
            foreach (int i in ints.Even())
            {
                Console.WriteLine(i);
            }

            Console.WriteLine("Iterating over the list in reversed order.");
            foreach (int i in ints.Reversed())
            {
                Console.WriteLine(i);
            }

            Console.WriteLine("Iterating over the list in random order.");
            foreach (int i in ints.Random())
            {
                Console.WriteLine(i);
            }

            Console.ReadLine();
        }
    }

    public static class ListExtensions
    {
        /// <summary>
        /// Iterates over the list only returns even numbers
        /// </summary>
        /// <param name="list"></param>
        public static IEnumerable<int> Even(this List<int> list)
        {
            foreach (var i in list)
            {
                if (i % 2 == 0)
                 {
                    yield return i;
                }
           }
        }

        /// <summary>
        /// Iterates over the list only returns odd numbers
        /// </summary>
        public static IEnumerable<int> Odd(this List<int> list)
        {
            foreach (var i in list)
            {
                if (i % 2 != 0)
                {
                    yield return i;
                }
            }
        }

        /// <summary>
        /// Iterates over the list in reversed order
        /// </summary>
        public static IEnumerable<int> Reversed(this List<int> list)
        {
            for (int i = list.Count; i >= 0; i--)
            {
                yield return i;
            }
        }

        /// <summary>
        /// Iterates over the list in random order
        /// </summary>
        public static IEnumerable<int> Random(this List<int> list)
        {
            // Initialize a random number generator with a seed.
            System.Random rnd =
                new Random((int)DateTime.Now.Ticks);

            // Create a list to keep track of which indexes we've
            // already returned
            List<int> visited =
                new List<int>();

            // loop until we've returned the value of all indexes
            // in the list
            while (visited.Count < list.Count)
            {
                int index =
                    rnd.Next(0, list.Count);

                // Make sure we've not returned it already
                if (!visited.Contains(index))
                {
                    visited.Add(index);
                    yield return list[index];
                }
            }
        }
    }
}

答案 4 :(得分:1)

我实际上喜欢使用LINQ的cfeduke方法,这让我觉得它让我不知所措。添加到我之前的示例中。如果你想在LINQ的帮助下进行奇数和偶数迭代,你可以使用

// Even
foreach (var i in ints.FindAll(number => number % 2 == 0))
{
      Console.WriteLine(i);
}

// Odd
foreach (var i in ints.FindAll(number => number % 2 != 0))
{
      Console.WriteLine(i);
}

答案 5 :(得分:1)

使用C5 Generic Collection Library中的IList<T>,反向迭代是一项功能,而非扩展:

foreach (var i in list.Reverse())
{
}

同样,您可以使用Shuffle()方法获得随机排序:

var listClone = (IList<T>) list.Clone();
listClone.Shuffle();
foreach (var i in listClone)
{
}

答案 6 :(得分:0)

你可以倒退:

for (int i=col.count-1; i>0; i--){ 
      DoSomething ( col.item[i]) ;
}

不确定确切的语法,但那是范例。

对于完全随机的顺序,您可以通过它的索引访问集合元素。为确保您点击每个项目,您需要跟踪已处理的元素(可能是通过复制集合然后在访问后删除元素)。

编辑:有关随机访问的更多详细信息 随机访问的代码可能如下所示:

 collection c = originalCollection;
 while (c.count > 0) {
     int i = randomNumber(seed) mod c.count
     element d = c[i];
     c.remove(d);
     DoSomething(d);
}

答案 7 :(得分:0)

您可以通过提供自己的比较器对列表进行排序并迭代该列表。

答案 8 :(得分:0)

你想要一个集合并与之互动吗?

如果是,请尝试:

Random rand = new Random(Environment.TickCount);

test.Sort((string v1, string v2) => {
                if (v1.Equals(v2))
                {
                    return 0;
                }

                int x = rand.Next();
                int y = rand.Next();

                if (x == y)
                {
                    return 0;
                }
                else if (x > y)
                {
                    return 1;
                }

                return -1; 
            });

for (string item in test)
{
  Console.WriteLn(item);
}
// Note that test is List<string>;

答案 9 :(得分:0)

从我对C#语言规范的阅读中,foreach迭代语句取决于正在迭代的struct / class /接口,并在其上定义了GetEnumerator()函数。 GetEnumerator()返回的对象必须将MoveNext()定义为成员函数。 MoveNext()定义为在第一次调用时访问列表中的“first”对象,然后在后续调用中访问“next”,返回true,直到列表中不存在其他元素,然后返回false。

Domenic所指的特征,yield return,首先出现在规范的2.0版本中,并且看起来似乎对此有用。对于1.1版,您唯一的选择是从您的基础派生一个新的struct / class /接口并覆盖GetEnumerator()以返回一个新的IEnumerator,其中MoveNext()函数将遵循选择第一个集合元素和任何的不同规则后续的收集元素。

我自己的建议是使用索引集合,然后使用带有适当索引计算的for循环(这里可以使用随机数生成器,如果需要,使用整数数组或其他一些技术来验证相同的索引如果必须在实际操作中这样做,则不会使用两次值。

答案 10 :(得分:0)

使用随机排序
http://www.dailycoding.com/..using_linq.aspx

List<Employee> list = new List<Employee>();

list.Add(new Employee { Id = 1, Name = "Davolio Nancy" });
list.Add(new Employee { Id = 2, Name = "Fuller Andrew" });
list.Add(new Employee { Id = 3, Name = "Leverling Janet" });
list.Add(new Employee { Id = 4, Name = "Peacock Margaret" });
list.Add(new Employee { Id = 5, Name = "Buchanan Steven" });
list.Add(new Employee { Id = 6, Name = "Suyama Michael" });
list.Add(new Employee { Id = 7, Name = "King Robert" });
list.Add(new Employee { Id = 8, Name = "Callahan Laura" });
list.Add(new Employee { Id = 9, Name = "Dodsworth Anne" });

list = list.OrderBy(emp => Guid.NewGuid()).ToList();