示例(货币是一个枚举):
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时,该类型的任何两个实例应该是可比较的,并且如果客户端代码尝试比较它们,则不应该抛出任何异常?
答案 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);
}
}
(当上面的Dollar
和PoundSterling
用作标记时,我并不完全满意它们,但是它允许我们使用new()
约束来防止MoneyQuantity<Currency>
是有效类型)
如果使用C#7.2或更高版本,则可以使Currency
的构造函数private protected
成为防止人们创建流氓Currency
类型的防范措施之一。
当然,这可能不适合您问题所在的所有部分,所以不要只是权衡利弊而直接参与进来。当然,F#的单位看起来好多了,但我没有在愤怒中使用它们。