假设我们有以下类型:
struct MyNullable<T> where T : struct
{
T Value;
public bool HasValue;
public MyNullable(T value)
{
this.Value = value;
this.HasValue = true;
}
public static implicit operator T(MyNullable<T> value)
{
return value.HasValue ? value.Value : default(T);
}
}
尝试编译以下代码片段:
MyNullable<int> i1 = new MyNullable<int>(1);
MyNullable<int> i2 = new MyNullable<int>(2);
int i = i1 + i2;
这剪得很好,没有错误。 i1和i2转换为整数和加法评估。
但如果我们有以下类型:
struct Money
{
double Amount;
CurrencyCodes Currency; /*enum CurrencyCode { ... } */
public Money(double amount, CurrencyCodes currency)
{
Amount = amount;
Currency = currency;
}
public static Money operator + (Money x, Money y)
{
if (x.Currency != y.Currency)
// Suppose we implemented method ConvertTo
y = y.ConvertTo(x.Currency);
return new Money(x.Amount + y.Amount, x.Currency);
}
}
尝试编译另一个代码段:
MyNullable<Money> m1 =
new MyNullable<Money>(new Money(10, CurrenciesCode.USD));
MyNullable<Money> m2 =
new MyNullable<Money>(new Money(20, CurrenciesCode.USD));
Money m3 = m1 + m2;
现在问题是,为什么编译器生成“错误CS0019:运算符'+'不能应用于'MyNullable&lt; Money&gt;'类型的操作数和'MyNullable&lt; Money&gt;'“?
答案 0 :(得分:10)
这是一个有趣的问题......例如,它适用于Decimal
,但不适用于TimeSpan
,它们都是正确的.NET类型(不像float
等原始类型)并且都有一个+运算符。好奇!
当然,你可以用以下方法扭动手臂:
Money m3 = (Money)m1 + (Money)m2;
它只是使用Nullable<T>
它当然可以免费工作 - 加上你得到编译器+运行时(拳击)支持。有理由不在这里使用Nullable<T>
吗?
我会看一下这个规格;在此期间,您可能会考虑将运营商推广到MyNullable<T>
;对于常规Nullable<T>
,C#编译器为类型支持的运算符提供“提升”运算符,但是你不能自己做。您可以做的最好的事情是提供所有明显的并且希望类型支持它;-p要使用泛型访问运算符,请参阅here,可以免费下载here。
请注意,您可能希望应用适当的“提升”检查 - 即
x + y => (x.HasValue && y.HasValue)
? new MyNullable<T>(x.Value + y.Value)
: new MyNullable<T>();
<强>更新强>
不同的处理看起来与14.7.4(ECMA 334 v4)“加法运算符”有关,其中它是为包括十进制的一系列类型预定义的(因此这是一个不好的测试由我来),因为通过14.2.4(相同)“二元运算符重载分辨率”,预定义的运算符确实得到特别提及。不过,我并不认为完全理解它。
答案 1 :(得分:9)
Marc是正确的 - 它是C#3.0规范中的第7.2.4节 - 二进制运算符重载决议。
基本上步骤是:
MyNullable<Money>
。MyNullable<T>
不会重载+。MyNullable<int> + MyNullable<int>
这是有效的,因为每个参数都隐式转换为int
- 但是当我们执行MyNullable<Money> + MyNullable<Money>
时,不会工作因为Money + Money
不在候选运算符集中。