C#是否为运行时分配了goto?
我正在创建一个C#代码,其中我希望使用goto,但不希望使用编译时常量,即使用字符串作为标签标识符C#是否为运行时分配了goto?,c#,.net,goto,C#,.net,Goto,我正在创建一个C#代码,其中我希望使用goto,但不希望使用编译时常量,即使用字符串作为标签标识符 顶部: 字符串label=“top”; //不起作用 后藤标签; //也不行 转到“顶端”; 我知道goto不是一个好的编程实践,这不是一个关于是否应该在应用程序中使用它的意见问题——这是一个从BASIC生成C代码的程序,它已经计算了goto。不,没有这样的问题。如果您真的需要,我可能会生成一个switch语句: switch (label): { case "top":
顶部:
字符串label=“top”;
//不起作用
后藤标签;
//也不行
转到“顶端”;
我知道
goto
不是一个好的编程实践,这不是一个关于是否应该在应用程序中使用它的意见问题——这是一个从BASIC生成C代码的程序,它已经计算了goto。不,没有这样的问题。如果您真的需要,我可能会生成一个switch语句:
switch (label):
{
case "top":
goto top;
case "bottom":
goto bottom;
// ...
}
如果您可以使用
字典
将代码分解为操作,那么这将是更干净的代码,可以在以后查看。。。但是如果你需要本地人在范围内等等,那么这个“讨厌的”代码可能是模仿基本行为的一种更简单的方法。不,它没有
请注意,下面的C示例代码专门用作BASIC-to-C编译器的输出格式。任何编写这种“真实”C#代码的人都将被吊销编程许可证。
BASIC的GOTO语句可以使用“tail call pessimization”实现:将每个GOTO X
转换为GOSUB X:RETURN
然后,您可以将整个基本程序呈现为一个巨大的Gosub(int-lineNumber)
函数,使用一个switch语句,每个行号都有一个case
块,使用一个Main
函数,只需将GOSUBs转换为最低行号即可
例如,基本程序:
10 PRINT "Enter an integer: ";
20 INPUT N%
30 IF N% MOD 2 = 0 THEN GOTO 60
40 PRINT N%;" is odd."
50 GOTO 70
60 PRINT N%;" is even."
70 PRINT "Goodbye."
可以逐行转换为C#程序,如下所示:
using System;
static class Program
{
// BASIC variables are always global and zero-initialized.
static int n_int = 0;
static void Gosub(int lineNumber)
{
switch (lineNumber)
{
case 10: // 10 PRINT "Enter an integer: ";N%
Console.Write("Enter an integer: ");
// By default, the end of each line falls thru to next line number
goto case 20;
case 20: // 20 INPUT N%
n_int = int.Parse(Console.ReadLine());
goto case 30;
case 30: // 30 IF N% MOD 2 = 0 THEN GOTO 60
if (n_int % 2 == 0)
{
Gosub(60);
return;
}
goto case 40;
case 40: // 40 PRINT N%;" is odd."
Console.WriteLine("{0} is odd.", n_int);
goto case 50;
case 50: // 50 GOTO 70
Gosub(70);
return;
case 60: // 60 PRINT N%;" is even."
Console.WriteLine("{0} is even.", n_int);
goto case 70;
case 70: // 70 PRINT "Goodbye."
Console.WriteLine("Goodbye.");
// Falling off the end of the program exits it.
return;
}
}
static void Main()
{
Gosub(10);
}
}
using System;
static class Program
{
// BASIC variables are always global and zero-initialized.
static int n_int = 0;
static void Gosub(int lineNumber)
{
START:
switch (lineNumber)
{
case 10: // 10 PRINT "Enter an integer: ";N%
Console.Write("Enter an integer: ");
// By default, the end of each line falls thru to next line number
goto case 20;
case 20: // 20 INPUT N%
n_int = int.Parse(Console.ReadLine());
goto case 30;
case 30: // 30 IF N% MOD 2 = 0 THEN GOTO 60
if (n_int % 2 == 0)
{
lineNumber = 60;
goto START;
}
goto case 40;
case 40: // 40 PRINT N%;" is odd."
Console.WriteLine("{0} is odd.", n_int);
goto case 50;
case 50: // 50 GOTO 70
lineNumber = 70;
goto START;
case 60: // 60 PRINT N%;" is even."
Console.WriteLine("{0} is even.", n_int);
goto case 70;
case 70: // 70 PRINT "Goodbye."
Console.WriteLine("Goodbye.");
// Falling off the end of the program exits it.
return;
}
}
static void Main()
{
Gosub(10);
Console.ReadKey();
}
}
当您有这样一个文字行号时,您可以优化Gosub(X);返回代码>转到<代码>转到案例X代码>。但是,函数调用方法允许行号是任意表达式,而C#的goto case
则不允许
转换后的代码显然不易维护,但它确实可以编译和运行
编辑:显然,C#编译器不能保证尾部调用优化,这可能会导致长循环或无限循环的堆栈溢出。但是,您可以通过重新分配lineNumber
参数并跳回到switch
语句的开头来手动执行此优化,如下所示:
using System;
static class Program
{
// BASIC variables are always global and zero-initialized.
static int n_int = 0;
static void Gosub(int lineNumber)
{
switch (lineNumber)
{
case 10: // 10 PRINT "Enter an integer: ";N%
Console.Write("Enter an integer: ");
// By default, the end of each line falls thru to next line number
goto case 20;
case 20: // 20 INPUT N%
n_int = int.Parse(Console.ReadLine());
goto case 30;
case 30: // 30 IF N% MOD 2 = 0 THEN GOTO 60
if (n_int % 2 == 0)
{
Gosub(60);
return;
}
goto case 40;
case 40: // 40 PRINT N%;" is odd."
Console.WriteLine("{0} is odd.", n_int);
goto case 50;
case 50: // 50 GOTO 70
Gosub(70);
return;
case 60: // 60 PRINT N%;" is even."
Console.WriteLine("{0} is even.", n_int);
goto case 70;
case 70: // 70 PRINT "Goodbye."
Console.WriteLine("Goodbye.");
// Falling off the end of the program exits it.
return;
}
}
static void Main()
{
Gosub(10);
}
}
using System;
static class Program
{
// BASIC variables are always global and zero-initialized.
static int n_int = 0;
static void Gosub(int lineNumber)
{
START:
switch (lineNumber)
{
case 10: // 10 PRINT "Enter an integer: ";N%
Console.Write("Enter an integer: ");
// By default, the end of each line falls thru to next line number
goto case 20;
case 20: // 20 INPUT N%
n_int = int.Parse(Console.ReadLine());
goto case 30;
case 30: // 30 IF N% MOD 2 = 0 THEN GOTO 60
if (n_int % 2 == 0)
{
lineNumber = 60;
goto START;
}
goto case 40;
case 40: // 40 PRINT N%;" is odd."
Console.WriteLine("{0} is odd.", n_int);
goto case 50;
case 50: // 50 GOTO 70
lineNumber = 70;
goto START;
case 60: // 60 PRINT N%;" is even."
Console.WriteLine("{0} is even.", n_int);
goto case 70;
case 70: // 70 PRINT "Goodbye."
Console.WriteLine("Goodbye.");
// Falling off the end of the program exits it.
return;
}
}
static void Main()
{
Gosub(10);
Console.ReadKey();
}
}
与前一个示例一样,常量GOTO
目标可以优化为单个GOTO case
语句。它没有iti。我认为,通过使用函数字典,允许您按字符串值分派到某些代码,可以实现相同的行为。当然,在处理变量和其他问题时会遇到一些重大挑战。这真是个好主意,谢谢!您可以保留一个行号字典,然后跳转到行号,基本上就是计算出goto。如果你想发布答案,我会接受。BASIC只支持转到行号吗?如果是这样的话,也许有一个更简单的方法,比如Jon Skeet发布的答案。我在寻找你所说的Atari BASIC的gotos。在我心目中是辛克莱的基础,但我没有一个确切的规格连接。天才!我对这些的唯一想法是在块内的goto,比如说,在for/if中。我想如果你不允许跳出或跳入一个块,你可以在for循环的开关盒中有一个嵌套函数?好吧,一个for
..NEXT
循环可以很容易地转换成WHILE
..WEND
循环,然后可以将其转换为GOTO
加IF
…GOTO
。仍然可能有一些奇怪的边缘情况跳入或跳出循环。好的,这导致了另一个问题。一个简单的程序,如10打印“你好”11转到10。(编译成一系列Gosub(10))由于大量递归导致StackOverflowException。我不确定这是否可以解决?有时,基本程序需要有无限循环,例如游戏。正如我在回答中提到的,如果基本的GOTO
目标是常数,你总是可以将其优化为一个简单的GOTO case
语句。只计算了需要调用GOSUB
的GOTO
(和GOSUB
)。由于行号为BASIC的代码没有局部变量,堆栈帧将相对便宜。谢谢。我还意识到,如果使用/optimize运行csc,堆栈不会溢出。可能是因为您在编辑开始时提到的内容。不过,我想我还是会尝试优化自己的常量和无限goto调用,因为它们非常常见。