简体   繁体   English

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

[英]How to create a enumerator/generator of strings or integers?

I want to have a enumerator/generator that will always provide me with the next value whenever I call say GetNext? 我想拥有一个枚举器/生成器,每当我打电话说GetNext时,它将始终为我提供下一个值。 Is this possible? 这可能吗?

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
...

I will not iterator on it in 1 place, but rather in many places and in very different times. 我不会在1个地方迭代它,而是在许多地方和非常不同的时间进行迭代。

How do I do this in the most elegant way? 如何以最优雅的方式做到这一点? If it's also fast, that's good too. 如果速度也很快,那也很好。

The term "enumerator" is a little misleading here, since that's a specific type of sequence which has a beginning and an end. 术语“枚举器”在这里有点误导,因为这是序列的特定类型,具有开头和结尾。 In your case, you are not enumerating anything per say, but just advancing to the next value indefinitely. 在您的情况下,您不是在逐一列举任何内容,而是无限期地前进到下一个值。 Sounds like a good use for a singleton : 听起来很适合单例

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;
    }
}

And used as: 并用作:

int value = NextNumberGenerator.Current.GetNext();

Edit: some commenters have pointed out that it's entirely possible to use an IEnumerable. 编辑:一些评论者指出,完全有可能使用IEnumerable。 However, I argue that's a misuse of IEnumerable simply because that case and this one appear similar in certain ways, but are not the same. 但是,我认为这是对IEnumerable的误用,仅是因为这种情况和这种情况在某些方面看起来相似,但并不相同。 Using an extremely specific, purposeful interface because one of the multiple things it does is similar (not same) to the thing you need will come back to bite you. 使用一个非常特定的,有目的的界面,因为它所做的多种事情之一(与您需要的事物相似(不相同))会再次咬住您。

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);

It's part of .net 它是.net的一部分

IEnumerable<T>.GetEnumerator()

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

EDIT: 编辑:

So with a bit of Linq magic, your code could be: 因此,使用一点Linq魔术,您的代码可能是:

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

I'm sure you can work the rest out for yourself. 我相信您可以自己解决其余的问题。

If you truely need an "infinite" enumeration, using the yield statement might also be a good fit, but the formatting of your string makes me think that 99 is a maximum 如果您确实需要“无限”枚举,则使用yield语句也可能很合适,但是字符串的格式使我认为最大为99

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

And to use it: 并使用它:

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

To use it without a foreach, then you can do so like this: 要在没有foreach的情况下使用它,则可以这样进行:

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

To satisfy Rex M: 要满足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();
}

It sounds like you actually want a static function, like this: 听起来您实际上想要一个静态函数,如下所示:

static class FileNames {
    static int nextNumber;

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

Note that this will start with one, not zero. 请注意,这将从1开始,而不是零。 To make it start with zero, initialize nextNumber to -1. 要使其从零开始, nextNumber初始化为-1。 EDIT : Or, remove the first line ( nextNumber++ ) and change the parameter in the second line to ++nextNumber . 编辑 :或,删除第一行( nextNumber++ )并将第二行中的参数更改为++nextNumber

Also, not that this is not thread-safe. 另外,并不是说这不是线程安全的。 If you want to call it on multiple threads, use Interlocked.Increment . 如果要在多个线程上调用它,请使用Interlocked.Increment

how about a simple thing like this? 这样简单的事情怎么样?

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;
    }
}

The simplest way to do this is to use iterators . 最简单的方法是使用迭代器 For example: 例如:

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";
}

You can also return an IEnumerable instead of an IEnumerator without chaning anything but the return type. 您也可以返回IEnumerable而不是IEnumerator而无需更改返回类型。 An IEnumerable is an object that creates IEnumerators that enumerate over it, and can be used in foreach loops. IEnumerable是创建枚举其上的IEnumerator的对象,并且可以在foreach循环中使用。 Note that if you return an IEnumerable, your code will run again every time one it is enumerated (by different enumerators). 请注意,如果返回IEnumerable,则每次被枚举一次(由不同的枚举器)时,代码都会再次运行。


To use it in different methods, you could share an IEnumerator between them in a static field. 要以不同的方法使用它,可以在静态字段中共享它们之间的IEnumerator。

Enumerators are nice because you can use them in foreach loops, and play around with them using LINQ in all sorts of fun ways. 枚举器很不错,因为您可以在foreach循环中使用它们,并以各种有趣的方式使用LINQ来使用它们。

Here's an example of an Enumerator doing the sort of thing you talk about in your original question: 这是一个枚举器执行您在原始问题中谈论的事情的示例:

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);
}

will produce: 将产生:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM