代码之家  ›  专栏  ›  技术社区  ›  Hammerite

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

  •  1
  • Hammerite  · 技术社区  · 6 年前

    示例(货币是枚举):

    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 回复  |  直到 6 年前
        1
  •  3
  •   Jon Hanna    6 年前

    我认为这是个问题,而不是不能接受的。很明显有东西 Sort 或任何需要 CompareTo() (包括用户可能没有意识到调用它的东西)对于那些“只起作用”的东西就没有那么有用了,所以这种可能性必须有很好的文档记录(错误信息有帮助)。但另一方面,它比“只是工作”的方式可能不太正确的东西要好。

    你迫使用户要么避免这样的比较,要么自己想办法进行比较(例如转换成一致的货币进行比较),但这也迫使他们确保按照对他们有用的条件进行比较(例如根据他们实际使用的汇率进行转换)。这可能是你能做的最好的平衡。

        2
  •  1
  •   Damien_The_Unbeliever    6 年前

    在过去,我经常把“单位”建模,比如这里的货币,作为类型,然后你就可以 MoneyQuantity 其中一种类型的泛型。(你可以做很多有趣的尝试来确保它被参数化的类型只是你想要支持的货币)。这会立即防止出现问题(因为 MoneyQuantity<Dollar> 可与其他 但不是为了 MoneyQuantity<PoundSterling> 或其他类型)。

    E、 g.那个 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);
        }
    }
    

    Dollar PoundSterling 在上面的构造中,当它们更多地用作标记时,但是它允许我们使用 new() MoneyQuantity<Currency> s是有效类型)

    Currency 的构造函数 private protected 作为防止人们制造流氓的守卫之一 货币

    当然,这可能不适合你的问题领域的所有部分,所以不要只是跳进去,这样做没有权衡利弊。当然,F单位看起来好多了,但我并没有生气地使用它们。