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倍

5123
0
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})