C# 是否具有运行时分配的 goto?
Does C# have runtime-assigned goto?
我正在创建一个 C# 程序,我想在其中使用 goto,而不是编译时常量,即。使用字符串作为标签标识符。
top:
string label = "top";
// Doesn't work
goto label;
// Doesn't work either
goto "top";
我知道 goto
不是一个好的编程习惯,这不是一个关于是否应该在应用程序中使用它的意见问题——它适用于从 BASIC 生成 C# 代码的程序,已经计算出 goto.
不,没有这样的事。如果你真的、真的需要这个,我可能会生成一个 switch 语句:
switch (label):
{
case "top":
goto top;
case "bottom":
goto bottom;
// ...
}
如果您 可以 将代码分解为带有 Dictionary<string, Action>
的操作,那将是以后查看的更清晰的代码...但是如果您需要本地化在范围等方面,那么此 "nasty" 代码可能是模仿 BASIC 行为的更简单方法。
请注意,下面的 C# 示例代码专门用作 BASIC-to-C# 编译器的输出格式。任何像这样编写“真正的”C# 代码的人都将被吊销其编程许可证。
BASIC 的 GOTO 语句可以使用“尾调用悲观化”来实现:将每个 GOTO X
转换为 GOSUB X:RETURN
.
然后,您可以将整个 BASIC 程序呈现为一个巨大的 Gosub(int lineNumber)
函数,使用带有每个行号的 case
块的 switch 语句,以及一个 Main
函数,它只是GOSUB 到最低行号。
因此,例如 BASIC 程序:
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);
}
}
当你有像这样的文字行号时,你可以将 Gosub(X); return;
优化为 goto case 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)
{
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
语句。
我正在创建一个 C# 程序,我想在其中使用 goto,而不是编译时常量,即。使用字符串作为标签标识符。
top:
string label = "top";
// Doesn't work
goto label;
// Doesn't work either
goto "top";
我知道 goto
不是一个好的编程习惯,这不是一个关于是否应该在应用程序中使用它的意见问题——它适用于从 BASIC 生成 C# 代码的程序,已经计算出 goto.
不,没有这样的事。如果你真的、真的需要这个,我可能会生成一个 switch 语句:
switch (label):
{
case "top":
goto top;
case "bottom":
goto bottom;
// ...
}
如果您 可以 将代码分解为带有 Dictionary<string, Action>
的操作,那将是以后查看的更清晰的代码...但是如果您需要本地化在范围等方面,那么此 "nasty" 代码可能是模仿 BASIC 行为的更简单方法。
请注意,下面的 C# 示例代码专门用作 BASIC-to-C# 编译器的输出格式。任何像这样编写“真正的”C# 代码的人都将被吊销其编程许可证。
BASIC 的 GOTO 语句可以使用“尾调用悲观化”来实现:将每个 GOTO X
转换为 GOSUB X:RETURN
.
然后,您可以将整个 BASIC 程序呈现为一个巨大的 Gosub(int lineNumber)
函数,使用带有每个行号的 case
块的 switch 语句,以及一个 Main
函数,它只是GOSUB 到最低行号。
因此,例如 BASIC 程序:
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);
}
}
当你有像这样的文字行号时,你可以将 Gosub(X); return;
优化为 goto case 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)
{
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
语句。