如何从方法的最后一个返回点恢复方法
本文关键字:方法 返回 恢复 最后一个 | 更新日期: 2023-09-27 18:20:02
我想知道C#中是否有某种机制允许从上次调用时到达的最后一个返回"恢复"方法。
我需要什么:我有一个抽象语法树(AST),它是由一个从源语言设计的解析器创建的。这个抽象语法树是一个"根类"的对象,它有其他类的字段实例等等。我需要做的是创建一个序列器,它接受这个抽象语法树并通过生成器创建其他语言的指令。指令序列以null结束。这可以通过生成器方法next()
实现,该方法调用一个动态计算下一条指令的定序器方法。换句话说,我不能探索整个抽象语法树,生成所有指令,并在每次调用next()
时逐一返回,但每次生成器调用next()时,我必须创建它们中的每一个。
示例:为了让你更好地理解这个问题,我将发布一个伪代码。
static void Main(string[] args)
{Parser parser = new Parser (//constructor stuff);
Sequencer sequencer = new Sequencer(parser.Parse()); //the mehtod Parse() generates the AST
Generator generator = new Generator(sequencer);
Instruction instruction = generator.next();
while(instruction)
{print instruction
instruction = generator.next();
}
}
重要提示:我希望您理解的最重要的一点是,next()
并不总是在某种迭代中调用,所以我认为foreach
和迭代器不是一个好的解决方案
这是因为为了使用iterotor,我最终不得不写一些类似的东西
foreach(instruction in next())
{//do stuff with instruction}
我不想那样做!
然而,我将让您了解next()
的结构:
Instruction next()
{ return sequencer.generate();}
因此generate()
:
Instruction generate():
{Insturction instr;
while(I explored the whole AST)
{if(//actual node in the AST is this kind)
instr=//generate this instruction
else if(//actual node in the AST is this other kind)
instr=//generate this other instruction
else and so on....
//if the actual node has some child, then it is new actual node
**yield return** instruction;
}
}
最复杂的部分是,我需要具有yield return
行为的东西(所以从我在下一个generate()
调用时离开的地方开始,但由于我之前解释的原因,没有使用迭代器。除此之外,在AST中移动是非常困难的,因为我不能显式引用实际节点的父节点(比如作为实际节点副本的字段)。
由于这还不够,您可以递归地调用generate
(例如,如果有某种迭代构造函数需要翻译)。
如何做到这一点?
使用yield return
实现主逻辑。这将创建一个枚举器。将其存储在类成员变量或其他永久性位置。
然后在返回普通对象的包装方法中使用该枚举器,在每次调用时从枚举器中提取一个新项。
事实上,yield
和IEnumerator<T>
确实满足了您的需求。需要注意的关键点是IEnumerable<T>
公开了GetEnumerator
方法,这正是您所需要的。
发电机:
public class Generator
{
//...
public IEnumerable<Instruction> Generate()
{
// ...
yield return new Instruction(...);
// ...
}
//...
}
一个关于如何按照你想要的方式使用它的例子。关键部分是你可以做generator.Generate().GetEnumerator();
:
var enumerator = generator.Generate().GetEnumerator();
while (enumerator.MoveNext())
{
var instruction = enumerator.Current;
// do something with instruction
if (steps > 10) //some dummy reason to stop ordinary iteration
break;
}
// Now process generator's results manually
if (!enumerator.MoveNext())
throw new InstructionMissingException(); // no more instructions left
var followingInstruction = enumerator.Current;
// ...
if (!enumerator.MoveNext())
throw new InstructionMissingException(); // no more instructions left
var theLastInstruction = enumerator.Current;
// ...
if (enumerator.MoveNext())
throw new TooMuchInstructionsException(); // unexpected instruction
我注意到GetEnumerator
可以在IEnumerable
上调用,这要归功于这个答案,它回答了一个类似的问题。
此外,正如阿列克谢·列文科夫在评论中指出的那样,你可以用一种更适合你需求的舒适方法来包装MoveNext
和Current
。您甚至可以为IEnumerator<Instruction>
类型编写一个扩展方法:
public static class IEnumeratorExtensions
{
public static Instruction NextInstruction(this IEnumerator<Instruction> @this)
{
if (@this.MoveNext())
{
return @this.Current;
}
return null; // or throw, or whatever you like
}
}