C# 如何创建字符串或整数的枚举器/生成器?
我想要一个枚举器/生成器,每当我调用say GetNext时,它总是向我提供下一个值?这可能吗C# 如何创建字符串或整数的枚举器/生成器?,c#,.net,collections,ienumerable,C#,.net,Collections,Ienumerable,我想要一个枚举器/生成器,每当我调用say GetNext时,它总是向我提供下一个值?这可能吗 Examples: myStringEnumerator.GetNext() -> returns "Identifier01.xml" myStringEnumerator.GetNext() -> returns "Identifier02.xml" myStringEnumerator.GetNext() -> returns "Identifier03.xml"
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
...
我不会在1个地方迭代它,而是在许多地方和非常不同的时间迭代
我该如何以最优雅的方式做到这一点?如果它也很快,那也很好。它是.net的一部分
IEnumerable<T>.GetEnumerator()
我相信你可以自己解决剩下的问题
如果您确实需要“无限”枚举,那么使用yield语句也可能是一个很好的选择,但是字符串的格式使我认为99是最大值,术语“枚举器”在这里有点误导,因为这是一种有开始和结束的特定序列类型。在您的例子中,您并不是每次都枚举任何内容,而是无限期地前进到下一个值。听起来是一个很好的单亲家庭用法:
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的误用,因为这个例子和这个例子在某些方面看起来很相似,但并不相同。使用一个非常具体、有目的的界面,因为它所做的多项事情中有一项与您所需要的事情相似(不相同),它会反过来伤害您。最简单的方法是使用。例如:
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";
}
公共静态IEnumerator GetNumbers(){
对于(int i=0;i<25;i+=3)
收益率i;
}
//您也可以在不使用循环的情况下执行此操作,如下所示:
公共静态IEnumerator GetNames(){
产生返回“Identifier01.xml”;
产生返回“Identifier02.xml”;
产生返回“Identifier03.xml”;
产生返回“Identifier04.xml”;
收益返回“Identifier05.xml”;
}
您还可以返回一个而不是一个,而无需更改除返回类型以外的任何内容。IEnumerable是一个创建IEnumerator的对象,IEnumerator在其上枚举,可用于foreach
循环。请注意,如果返回IEnumerable,则每次(由不同的枚举器)枚举一个IEnumerable时,代码都将再次运行
要在不同的方法中使用它,可以在静态字段中共享IEnumerator
public IEnumerable<string> GetFileNames()
{
for (int i = 1; i <= 99; i++)
{
yield return string.Format("Identifier{0:00}.xml", i);
}
}
要在没有foreach的情况下使用它,您可以这样做:
IEnumerator<string> names = GetFileNames().GetEnumerator();
names.MoveNext();
string name1 = names.Current;
names.MoveNext();
string name2 = names.Current;
// etc, etc.
static class FileNames {
static int nextNumber;
public static string NextName() {
nextNumber++;
return String.Format(CultureInfo.InvariantCulture, "Indentifier{0:00}.xml", nextNumber);
}
}
IEnumerator name=getFileName().GetEnumerator();
names.MoveNext();
string name1=names.Current;
names.MoveNext();
string name2=names.Current;
//等等等等。
要满足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();
}
IEnumerator filenames=GetFileNames().GetEnumerator();
while(filename.MoveNext())
{
如果(!File.Exists(filenames.Current))
打破
}
while(haveFilesToWrite)
{
//使用filenames.Current打开并写入文件
filenames.MoveNext();
}
枚举数很好,因为您可以在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);
}
听起来您实际上想要一个静态函数,如下所示:
IEnumerator<string> names = GetFileNames().GetEnumerator();
names.MoveNext();
string name1 = names.Current;
names.MoveNext();
string name2 = names.Current;
// etc, etc.
static class FileNames {
static int nextNumber;
public static string NextName() {
nextNumber++;
return String.Format(CultureInfo.InvariantCulture, "Indentifier{0:00}.xml", nextNumber);
}
}
请注意,这将从1开始,而不是从零开始。要使其以零开始,请将nextNumber
初始化为-1编辑:或者,删除第一行(nextNumber++
),并将第二行中的参数更改为++nextNumber
而且,这并不是说它不是线程安全的。如果要在多个线程上调用它,请使用。像这样简单的事情怎么样
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;
}
}
公共部分类表单1:表单
{
私有void Form1\u加载(对象发送方、事件参数e)
{
myGen myStringEnumerator=新的myGen(myGen.wanted.nextNum);
myGen evenNumberGenerator=新的myGen(myGen.wanted.EvenNum);
对于(int i=0;我很感谢,但是如何生成我上面发布的那些值呢?我认为OP使用的“枚举器”一词有点误导。请看我的答案。感谢spender,是的,可能是字符串,但对于偶数,我希望它是无限的,但您的回答很好。对于偶数的“无限”供应(无论如何,最大值为Int32!):Enumerable.Range(0,Int32.MaxValue)。其中(n=>n%2==0)。GetEnumerator()。如果确实需要,请使用Int64。如果确实需要“无限”您需要从某个地方获得BigInteger实现并编写迭代器方法。如果这是您的意思,Range不会预先生成大量集合。在这种情况下,Range的内存使用量将是最小的。在生成的IEnumerable上调用ToList或ToArray可能会有问题。谢谢,因此我不需要实现IEnumerable,对吗?我可以吗?如果可以的话,这会使单例变短,但不确定是否可行。所以我可以说yield return String.Format(“Identifier{0}.xml”,val++);这主意不错,但为什么不删除“current”部分并直接在getnext中使用它呢?所以NextNumber Generator.getnext()@Rex:我完全不同意。IEnumerable
不需要在foreach
中使用,也不需要无限,即使在这种情况下,如果你在处理完序列后小心使用break
。Rex:也许我误解了你,但我不确定Matthew的评论“如何绕过IEnumerable模式的一部分”IEnumerable不要求结束,无限枚举数非常有用,因为它们允许调用代码施加自己的终止条件,而不是将其烘焙到枚举数中。例如,foreach(AllPrimes()中的BigInteger p)。其中(p@Rex我不同意,无限集仍然是合法集,可以合法地迭代。你比我快了30秒!谢谢Matthew。我如何在多个地方使用它?比如GetFileNames()。接下来在method0中,稍后在method1 GetFileNames()中).Next,然后是method2内部,等等?代码的不同部分要求下一个元素。我不确定这是否是个好主意-为每个迭代执行n个I/O操作?@Rex:我假设将使用第一个示例
static class FileNames {
static int nextNumber;
public static string NextName() {
nextNumber++;
return String.Format(CultureInfo.InvariantCulture, "Indentifier{0:00}.xml", nextNumber);
}
}
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;
}
}