从IComparable.CompareTo()抛出是否可以接受?

时间:2018-08-23 10:49:25

标签: c# .net exception icomparable

示例(货币是一个枚举):

public struct MoneyQuantity: IComparable<MoneyQuantity>
{
    public Currency Currency { get; }
    public decimal  Amount   { get; }

    public MoneyQuantity(Currency currency, decimal amount)
    {
        Currency = currency;
        Amount   = amount;
    }

    public int CompareTo(MoneyQuantity that)
        => Currency == that.Currency ?
            Amount.CompareTo(that.Amount) :
            throw new InvalidOperationException("Can't compare quantities of money in different currencies");
}

这可以接受吗?还是可以预期,当一个类型是IComparable时,该类型的任何两个实例应该是可比较的,并且如果客户端代码尝试比较它们,则不应该抛出任何异常?

2 个答案:

答案 0 :(得分:3)

我认为这是一个问题,而不是不可接受的。显然,抛出Sort的东西或调用CompareTo()的任何操作(包括用户可能没有意识到对其的调用的东西)将比“正常工作”的东西有用,因此可能性将必须有据可查(错误消息有帮助)。但是,另一方面,它比以某种不太正确的方式“有效”的东西要好。

您正在强迫用户要么避免进行此类比较,要么设法自己进行比较(例如,转换为用于比较的统一货币),但是这也迫使他们必须确保自己在对他们有用的期限内这样做( (例如,根据他们实际使用的汇率进行此类转化)。总之,这可能是您最好的选择。

答案 1 :(得分:1)

过去我一直在模拟“单位”(例如此处的货币)作为类型,然后将MoneyQuantity设为其中一种类型的通用名称。 (您可以做很多花哨的尝试,以确保对其进行参数化的类型只是您要支持的货币)。这样就可以立即解决问题(因为MoneyQuantity<Dollar>可以与其他MoneyQuantity<Dollar>相提并论,而不能与MoneyQuantity<PoundSterling>或任何其他类型相提并论)。

例如下面的Console.WriteLine行无法编译,但是其余的很好:

using System;

namespace PlayAreaCSCon
{
    class Program
    {
        static void Main(string[] args)
        {
            var d = new MoneyQuantity<Dollar>(1);
            var p = new MoneyQuantity<PoundSterling>(1);

            Console.WriteLine(p.CompareTo(d));
        }
    }
    public abstract class Currency
    {
        protected Currency() { }
    }
    public class Dollar : Currency
    {
        public Dollar() : base() { }
    }
    public class PoundSterling : Currency
    {
        public PoundSterling() : base() { }
    }
    public struct MoneyQuantity<TCurrency> : 
           IComparable<MoneyQuantity<TCurrency>>
           where TCurrency : Currency, new()
    {
        public decimal Amount { get; }

        public MoneyQuantity(decimal amount)
        {
            Amount = amount;
        }

        public int CompareTo(MoneyQuantity<TCurrency> that)
                => Amount.CompareTo(that.Amount);
    }
}

(当上面的DollarPoundSterling用作标记时,我并不完全满意它们,但是它允许我们使用new()约束来防止MoneyQuantity<Currency>是有效类型)

如果使用C#7.2或更高版本,则可以使Currency的构造函数private protected成为防止人们创建流氓Currency类型的防范措施之一。

当然,这可能不适合您问题所在的所有部分,所以不要只是权衡利弊而直接参与进来。当然,F#的单位看起来好多了,但我没有在愤怒中使用它们。