C# 在某些特定场景中,用作控制流机制的异常是否有效?
我正在调整C# 在某些特定场景中,用作控制流机制的异常是否有效?,c#,.net,exception-handling,C#,.net,Exception Handling,我正在调整RationalNumber实现中的一些代码。特别是在平等逻辑中,我考虑了以下几点: public bool Equals(RationalNumber other) { if (RationalNumber.IsInfinity(this) || RationalNumber.IsInfinity(other) || RationalNumber.IsNaN(this) || RationalNumber.IsNaN(other))
RationalNumber
实现中的一些代码。特别是在平等逻辑中,我考虑了以下几点:
public bool Equals(RationalNumber other)
{
if (RationalNumber.IsInfinity(this) ||
RationalNumber.IsInfinity(other) ||
RationalNumber.IsNaN(this) ||
RationalNumber.IsNaN(other))
{
return false;
}
try
{
checked
{
return this.numerator * other.Denominator == this.Denominator * other.numerator;
}
}
catch (OverflowException)
{
var thisReduced = RationalNumber.GetReducedForm(this);
var otherReduced = RationalNumber.GetReducedForm(other);
return (thisReduced.numerator == otherReduced.numerator) && (thisReduced.Denominator == otherReduced.Denominator);
}
}
如您所见,我使用异常作为流控制机制。这背后的原因是,我不想在每次等式检查中计算两个分数的最大公约数而招致惩罚。因此,我只决定在可能性最小的情况下执行此操作:一个或两个交叉积溢出
这是可以接受的做法吗?我一直在读到,异常永远不应该被用作代码的流机制,但我真的看不到另一种方法来实现我想要的
欢迎使用任何替代方法。通常捕获异常会带来很高的开销,如果您能做些什么,您应该捕获异常 在您的情况下,您可以对异常采取一些措施。在我看来,将其用作控制流不是问题,但我建议您实现逻辑(检查不同的条件以防止异常)然后对这两个选项进行基准测试并比较性能,因为通常捕获异常会带来很高的开销,但是如果检查以防止异常需要更多的时间,那么处理异常是更好的方法 由于OPs评论而更新(这是一个新的实现,我们没有使用.NET framework的Rational。分子和分母的类型是
long
)
您可以使用更大的类型来防止溢出异常,如decimal
或
由于评论而更新:
一个显示异常开销的简单示例
const int Iterations = 100000;
var sw = new Stopwatch();
var sum1 = 0;
sw.Start();
for (int i = 0; i < Iterations; i++)
{
try
{
var s = int.Parse("s" + i);
sum1 += s;
}
catch (Exception)
{
}
}
sw.Stop();
Console.WriteLine(sw.ElapsedMilliseconds);
Console.WriteLine(sum1);
var sw2 = new Stopwatch();
var sum2 = 0;
sw2.Start();
for (int i = 0; i < Iterations; i++)
{
try
{
int s;
if (int.TryParse("s" + i, out s))
sum2 += s;
}
catch (Exception)
{
}
}
sw2.Stop();
Console.WriteLine(sw2.ElapsedMilliseconds);
Console.WriteLine(sum2);
const int迭代次数=100000;
var sw=新秒表();
var-sum1=0;
sw.Start();
对于(int i=0;i
结果是:处理异常的速度至少要慢170倍
51230
30
0
MSDN中介绍了这种方法。 但捕获异常的开销很高,因为进程模式可能会在那个时候将用户模式更改为内核模式 这背后的原因是,我不想在每次等式检查中计算两个分数的最大公约数而招致惩罚 这是合理的推理。此代码的总成本为
{probability of fast-path} * {fast-path cost}
+ ((1.0 - {probability of fast-path}) * {slow-path cost})
根据所涉及的三个常数,这将是一个好的或坏的选择。你需要很好地理解数据将在实践中得到处理
请注意,异常非常缓慢。我曾经将它们的基准测试为每个CPU核心每秒10000个,由于涉及内部CLR锁,我不确定它们是否会扩展到多个核心
也许您可以添加运行时评测。跟踪异常率。如果过高,请关闭优化
你可能应该记录下你为什么这么做
这也不是一个架构问题,因为如果您以后改变主意,您可以很容易地切换到不同的算法
作为替代方案,您可以首先计算并比较未选中项。如果结果为“不相等”,则可以保证确切结果也为“不相等”。即使发生溢出。因此,如果许多数字不相等,那么这可能是一条无异常的快速路径。我会使用它,但有时我会编写脏代码……抛接球的惩罚可能远远超过GCD评估的惩罚,请看这里:您是在实现
RationalNumber
还是在使用?@rbaryyoung:我看不出这里抛出异常的位置。他只是在处理一个罕见的例外情况。@TimSchmelter说得很好,不过我想这取决于数据集和溢出的实际情况。谢谢你的否决票。请你解释一下原因,这样我可以改进答案,让社区变得更好。谢谢你的意见。我的问题是:如果我的“快速”平等性检查将溢出,为了避免捕获异常,我还需要知道哪些其他选择?我没有看到任何异常。你的问题标题是“作为控制流机制使用的异常在某些特定场景中是否有效?”最后你说“欢迎任何替代方法”。异常没有“高开销”@我听不懂你最后的评论。我引用您的回答:“(…)我建议您实现逻辑(检查不同的条件以防止异常),然后对两个选项进行基准测试并比较性能”。因此,它回避了一个问题:我应该实现什么逻辑来防止异常?
{probability of fast-path} * {fast-path cost}
+ ((1.0 - {probability of fast-path}) * {slow-path cost})