C# 在现代.NET代码中使用goto有什么理由吗?
我刚刚在.NET基本库的reflector中找到了这段代码C# 在现代.NET代码中使用goto有什么理由吗?,c#,.net,goto,C#,.net,Goto,我刚刚在.NET基本库的reflector中找到了这段代码 if (this._PasswordStrengthRegularExpression != null) { this._PasswordStrengthRegularExpression = this._PasswordStrengthRegularExpression.Trim(); if (this._PasswordStrengthRegularExpression.Length
if (this._PasswordStrengthRegularExpression != null)
{
this._PasswordStrengthRegularExpression = this._PasswordStrengthRegularExpression.Trim();
if (this._PasswordStrengthRegularExpression.Length == 0)
{
goto Label_016C;
}
try
{
new Regex(this._PasswordStrengthRegularExpression);
goto Label_016C;
}
catch (ArgumentException exception)
{
throw new ProviderException(exception.Message, exception);
}
}
this._PasswordStrengthRegularExpression = string.Empty;
Label_016C:
... //Other stuff
我听过所有的斯皮尔的“你不应该因为害怕永远被流放到地狱而使用goto”。我一直对coders女士给予相当高的评价,虽然我可能不同意他们的所有决定,但我始终尊重他们的推理
那么,我缺少这样的代码有什么好的理由吗?这段代码摘录是由一个不称职的开发人员编写的吗?或者.NET reflector返回的代码不准确
我希望有一个好的理由,我只是盲目地错过了
感谢大家的投入我还没有在写过和审阅过的许许多多行.NET代码中看到Goto的有效案例
在不支持使用finally块进行结构化异常处理的语言中(PASCAL是结构化编程语言和经典C语言的祖父),战术上使用GOTO可以使代码在嵌套循环内执行终止时执行清理时更易于理解(与正确设置多个循环终止条件相反)。即使在那一天,我个人也没有因为这个原因使用goto(可能是因为害怕“永远流放到地狱里”)。你不应该看反射器代码 虽然如果你看过反汇编的IL,你会看到到处都是gotos。本质上,我们使用的所有循环和其他控制结构都转换成了gotos,只是通过将它们转换成代码中的结构,它变得更可读,更易于维护
顺便说一句,我不认为您发布的代码是使用goto的好地方,我很难想到一个。有一个有效的例子——当您试图模拟递归过程调用并在非递归代码中返回时,或者做类似的事情时(这种需求也发生在Prolog解释器中)但是一般来说,除非你正在做一些需要微优化的事情,比如象棋程序或语言解释器,否则最好只使用常规过程堆栈和函数/过程调用。它可能不在源代码中,这只是反汇编代码的外观。不,没有很好的理由使用
goto
。我上一次编写一个goto
语句是在1981年,从那以后我就没有错过过这个特殊的构造。我见过goto用来打破嵌套循环:
我不认为这样使用它有什么错。除了所有这些好的有效的东西,当你在查看反汇编代码时,请记住,开发人员可能在这些程序集上使用了模糊处理程序。模糊处理的一种技术是向IL添加随机goto。看看状态图。如果你相信要使用的最佳代码结构是最直接、最清楚地表达您的意图的代码结构,那么这些状态转换中的每一个都应该被编码为goto 然而,在现实世界中,这种情况往往会出现故障。第一个问题是,我们经常需要停止机器,退出到其他代码,然后重新启动机器——这意味着这些转换中的每一个都是对状态变量的更改,用于在switch/case语句中标识正确的状态。这实际上只是一种隐藏方式延迟goto-写入状态变量与写入程序计数器寄存器没有太大区别,实际上,这只是实现“goto在那里-但不是现在,以后”的一种方法 不过,在某些情况下,goto可以很好地表达某种状态模型中正在发生的事情——我猜一个例子就是医生有时使用的诊断流程图。如果你将其中一个作为一个程序来实现,而不使用goto进行转换,那么实际上你只是在给自己制造麻烦加密代码的意图
只是到目前为止,最常见的情况不太可能是手工编写的代码。我已经编写了代码生成器,为各种状态模型(决策处理、常规语法解析等)中的转换生成goto语句但是我不记得上一次我在手写代码中使用goto是什么时候了。我在编写FORTRAN时甚至都没有使用GO TO back进行过编码
我从未使用过它。我不明白为什么任何现代语言都会要求用户使用这样的东西。我会毫不含糊地说“不”.goto对于C等语言中的清理内容是非常有效的,至少在某种程度上它模拟了异常的概念。我确信.NET有更好的方法来处理类似的内容,所以goto已经过时并且容易出错。goto在编写解析器和词法分析器时经常很有用。Reflector并不完美此方法的所有代码可从参考源获得。它位于ndp\fx\src\xsp\system\web\security\admembershipprovider.cs中:
if( passwordStrengthRegularExpression != null )
{
passwordStrengthRegularExpression = passwordStrengthRegularExpression.Trim();
if( passwordStrengthRegularExpression.Length != 0 )
{
try
{
Regex regex = new Regex( passwordStrengthRegularExpression );
}
catch( ArgumentException e )
{
throw new ProviderException( e.Message, e );
}
}
}
else
{
passwordStrengthRegularExpression = string.Empty;
}
请注意,它是如何检测不到最后一个else子句并用goto进行补偿的。几乎可以肯定,它是被if()语句中的try/catch块绊倒的
很明显,您希望使用实际的源代码,而不是反编译版本。注释本身非常有用,您可以相信源代码是准确的。好吧,大多数情况下是准确的,有一个错误的后处理工具删除了Microsoft程序员的名字,造成了一些轻微的损坏。标识符有时是r用破折号替换,代码重复两次。您可以下载源代码。我不喜欢那个代码
我更愿意将正则表达式存储在成员中,并在设置时对其进行验证,从而避免在读取时对逻辑的所有需要。关于这一点: 那么,是否有充分的理由编写代码 就像我错过的那样?是吗 代码摘录由一个 糟糕的开发人员?还是.NET reflector 返回不准确的代码 我不同意这个观点
int i = 1;
switch (i)
{
case 1:
printf ("Case 1\r\n");
case 2:
printf ("Case 2\r\n");
default:
printf ("Default Case\r\n");
break;
}
Case 1
Case 2
Default Case
int i = 1;
switch (i)
{
case 1:
Console.Writeline ("Case 1");
case 2:
Console.Writeline ("Case 2");
default:
Console.Writeline ("Default Case");
break;
}
Control cannot fall through from one case label ('case 1:') to another
int i = 1;
switch (i)
{
case 1:
Console.WriteLine ("Case 1");
goto case 2;
case 2:
Console.WriteLine("Case 2");
goto default;
default:
Console.WriteLine("Default Case");
break;
}
private IDynamic ToExponential(Engine engine, Args args)
{
var x = engine.Context.ThisBinding.ToNumberPrimitive().Value;
if (double.IsNaN(x))
{
return new StringPrimitive("NaN");
}
var s = "";
if (x < 0)
{
s = "-";
x = -x;
}
if (double.IsPositiveInfinity(x))
{
return new StringPrimitive(s + "Infinity");
}
var f = args[0].ToNumberPrimitive().Value;
if (f < 0D || f > 20D)
{
throw new Exception("RangeError");
}
var m = "";
var c = "";
var d = "";
var e = 0D;
var n = 0D;
if (x == 0D)
{
f = 0D;
m = m.PadLeft((int)(f + 1D), '0');
e = 0;
}
else
{
if (!args[0].IsUndefined) // fractionDigits is supplied
{
var lower = (int)Math.Pow(10, f);
var upper = (int)Math.Pow(10, f + 1D);
var min = 0 - 0.0001;
var max = 0 + 0.0001;
for (int i = lower; i < upper; i++)
{
for (int j = (int)f;; --j)
{
var result = i * Math.Pow(10, j - f) - x;
if (result > min && result < max)
{
n = i;
e = j;
goto Complete;
}
if (result <= 0)
{
break;
}
}
for (int j = (int)f + 1; ; j++)
{
var result = i * Math.Pow(10, j - f) - x;
if (result > min && result < max)
{
n = i;
e = j;
goto Complete;
}
if (result >= 0)
{
break;
}
}
}
}
else
{
var min = x - 0.0001;
var max = x + 0.0001;
// Scan for f where f >= 0
for (int i = 0;; i++)
{
// 10 ^ f <= n < 10 ^ (f + 1)
var lower = (int)Math.Pow(10, i);
var upper = (int)Math.Pow(10, i + 1D);
for (int j = lower; j < upper; j++)
{
// n is not divisible by 10
if (j % 10 == 0)
{
continue;
}
// n must have f + 1 digits
var digits = 0;
var state = j;
while (state > 0)
{
state /= 10;
digits++;
}
if (digits != i + 1)
{
continue;
}
// Scan for e in both directions
for (int k = (int)i; ; --k)
{
var result = j * Math.Pow(10, k - i);
if (result > min && result < max)
{
f = i;
n = j;
e = k;
goto Complete;
}
if (result <= i)
{
break;
}
}
for (int k = (int)i + 1; ; k++)
{
var result = i * Math.Pow(10, k - i);
if (result > min && result < max)
{
f = i;
n = j;
e = k;
goto Complete;
}
if (result >= i)
{
break;
}
}
}
}
}
Complete:
m = n.ToString("G");
}
if (f != 0D)
{
m = m[0] + "." + m.Substring(1);
}
if (e == 0D)
{
c = "+";
d = "0";
}
else
{
if (e > 0D)
{
c = "+";
}
else
{
c = "-";
e = -e;
}
d = e.ToString("G");
}
m = m + "e" + c + d;
return new StringPrimitive(s + m);
}
class Program
{
// Calculate (20!) 1 million times using both methods.
static void Main(string[] args)
{
Stopwatch sw = Stopwatch.StartNew();
Int64 result = 0;
for (int i = 0; i < 1000000; i++)
result += FactR(20);
Console.WriteLine("Recursive Time: " + sw.ElapsedMilliseconds);
sw = Stopwatch.StartNew();
result = 0;
for (int i = 0; i < 1000000; i++)
result += FactG(20);
Console.WriteLine("Goto Time: " + sw.ElapsedMilliseconds);
Console.ReadLine();
}
// Recursive Factorial
static Int64 FactR(Int64 i)
{
if (i <= 1)
return 1;
return i * FactR(i - 1);
}
// Recursive Factorial (using GOTO)
static Int64 FactG(Int64 i)
{
Int64 result = 1;
Loop:
if (i <= 1)
return result;
result *= i;
i--;
goto Loop;
}
Recursive Time: 820
Goto Time: 259