如何创建字符串或整数的枚举器/生成器?

时间:2009-10-21 01:22:33

标签: c# .net collections ienumerable

我希望有一个枚举器/生成器,每当我调用GetNext时,它总会为我提供下一个值?这可能吗?

Examples:

myStringEnumerator.GetNext()
  -> returns "Identifier01.xml"
myStringEnumerator.GetNext()
  -> returns "Identifier02.xml"
myStringEnumerator.GetNext()
  -> returns "Identifier03.xml"
myStringEnumerator.GetNext()
  -> returns "Identifier04.xml"
myStringEnumerator.GetNext()
  -> returns "Identifier05.xml"
...

evenNumberGenerator.GetNext()
  -> returns 0
evenNumberGenerator.GetNext()
  -> returns 2
evenNumberGenerator.GetNext()
  -> returns 4
evenNumberGenerator.GetNext()
  -> returns 6
evenNumberGenerator.GetNext()
  -> returns 8
evenNumberGenerator.GetNext()
  -> returns 10
...

我不会在一个地方进行迭代,而是在许多地方和非常不同的时间进行迭代。

我如何以最优雅的方式做到这一点?如果它也快,那也很好。

8 个答案:

答案 0 :(得分:5)

术语“枚举器”在这里有点误导,因为这是一种具有开头和结尾的特定类型的序列。在你的情况下,你不是枚举每个说法,而只是无限期地推进到下一个值。听起来非常适合单身

class NextNumberGenerator
{
    private static NextNumberGenerator instance = new NextNumberGenerator();

    public static NextNumberGenerator Current
    {
        get { return instance; }
    }

    private int currentNumber = 0;

    public int GetNext()
    {
        return ++currentNumber;
    }

    public int GetNextEven()
    {
        currentNumber++;
        return (currentNumber % 2 == 0) ? currentNumber : ++currentNumber;
    }

    public int GetNextOdd()
    {
        currentNumber++;
        return (currentNumber % 2 != 0) ? currentNumber : ++currentNumber;
    }
}

用作:

int value = NextNumberGenerator.Current.GetNext();

编辑:一些评论者指出,完全有可能使用IEnumerable。但是,我认为这是对IEnumerable的误用,仅仅是因为这种情况和这种情况在某些方面看似相似,但却不一样。使用一个非常具体,有目的的界面,因为它所做的多件事情中的一件与你需要的东西相似(不相同)会回来咬你。

答案 1 :(得分:3)

var stringEnumerator = Enumerable.Range(1, 99).Select(i => string.Format("Identifier{0:00}.xml", i));
var numberEnumerator = Enumerable.Range(0, int.MaxValue/2).Select(i => 2*i);

答案 2 :(得分:2)

它是.net

的一部分
IEnumerable<T>.GetEnumerator()

http://msdn.microsoft.com/en-us/library/s793z9y2.aspxhttp://msdn.microsoft.com/en-us/library/78dfe2yb.aspx

编辑:

所以有了Linq魔法,你的代码可能是:

var myStringEnumerator= Enumerable
                        .Range(1,99)
                        .Select(n=>"Identifier"+n.ToString("00")+".xml")
                        .GetEnumerator();
if(myStringEnumerator.MoveNext())
    //myStringEnumerator.Current;

我相信你可以为自己完成剩下的工作。

如果你真的需要一个“无限”的枚举,使用yield语句也可能是一个不错的选择,但你的字符串格式化让我觉得99是最大的

答案 3 :(得分:2)

public IEnumerable<string> GetFileNames()
{
    for (int i = 1; i <= 99; i++)
    {
        yield return string.Format("Identifier{0:00}.xml", i);
    }
}

使用它:

string filename;
foreach (string tmp in GetFileNames())
{
    if (!File.Exists(tmp))
    {
        filename = file;
        break;
    }
}

要在没有foreach的情况下使用它,那么你可以这样做:

IEnumerator<string> names = GetFileNames().GetEnumerator();
names.MoveNext();
string name1 = names.Current;
names.MoveNext();
string name2 = names.Current;
// etc, etc.

满足Rex M:

IEnumerator<string> filenames = GetFileNames().GetEnumerator();
while (filenames.MoveNext())
{
    if (!File.Exists(filenames.Current))
        break;
}

while(haveFilesToWrite)
{
    // Use filenames.Current to open a file and write it
    filenames.MoveNext();
}

答案 4 :(得分:1)

听起来你真的想要一个静态函数,如下所示:

static class FileNames {
    static int nextNumber;

    public static string NextName() {
        nextNumber++;
        return String.Format(CultureInfo.InvariantCulture, "Indentifier{0:00}.xml", nextNumber);
    }
}

请注意,这将从一个开始,而不是零。要使其从零开始,请将nextNumber初始化为-1。 编辑:或者,删除第一行(nextNumber++)并将第二行中的参数更改为++nextNumber

另外,这不是线程安全的。如果要在多个线程上调用它,请使用Interlocked.Increment

答案 5 :(得分:1)

这样的简单事情怎么样?

public partial class Form1 : Form
{
    private void Form1_Load(object sender, EventArgs e)
    {
        myGen myStringEnumerator = new myGen(myGen.wanted.nextNum);
        myGen evenNumberGenerator = new myGen(myGen.wanted.EvenNum);

        for (int i = 0; i <= 5; i++)
        {
            MessageBox.Show(string.Format("Identifier{0}.xml", myStringEnumerator.GetNext().ToString("00")));
        }

        for (int i = 0; i <= 5; i++)
        {
            MessageBox.Show(evenNumberGenerator.GetNext().ToString());
        }
    }
}

public class myGen
{
    private int num = 0;
    public enum wanted
    {
        nextNum,
        EvenNum,
        OddNum
    }
    private wanted _wanted;

    public myGen(wanted wanted)
    {
        _wanted = wanted;
    }

    public int GetNext()
    {
        num++;
        if (_wanted == wanted.EvenNum)
        {
            if (num % 2 == 1)
            {
                num++;
            }
        }
        else if (_wanted == wanted.OddNum)
        {
            if (num % 2 == 0)
            {
                num++;
            }
        }
        return num;
    }
}

答案 6 :(得分:0)

最简单的方法是使用iterators。例如:

public static IEnumerator<int> GetNumbers() {
    for (int i = 0; i < 25; i += 3)
        yield return i;
}

//You can also do it without a loop, like this:
public static IEnumerator<string> GetNames() {
    yield return "Identifier01.xml";
    yield return "Identifier02.xml";
    yield return "Identifier03.xml";
    yield return "Identifier04.xml";
    yield return "Identifier05.xml";
}

除了返回类型之外,您还可以返回IEnumerable而不是IEnumerator而不返回任何内容。 IEnumerable是一个对象,它创建枚举它的IEnumerator,并且可以在foreach循环中使用。请注意,如果您返回IEnumerable,则每次枚举它时,您的代码都会再次运行(由不同的枚举器)。


要在不同的方法中使用它,您可以在静态字段中共享它们之间的IEnumerator。

答案 7 :(得分:0)

枚举器很好,因为你可以在foreach循环中使用它们,并以各种有趣的方式使用LINQ来玩它们。

这是一个枚举器的例子,你在原来的问题中做了一些事情:

static class MyEnumerators 
{
    IEnumerable <string> NumberedStringEnumerator (string format, int start, int count)
    {
        for ( int n = 0; n < count; ++n )
            yield return string.Format (format, start + n);
    }
}

static void Main ()
{
    foreach ( string s in NumberedStringEnumerator ("Identifier{0:2D}.xml", 1, 5) )
        Console.WriteLine (s);
}

将产生:

Identifier01.xml
Identifier02.xml
Identifier03.xml
Identifier04.xml
Identifier05.xml