(The "user-defined" in the title refers to the fact that addition and subtraction of TimeSpan
and DateTime
are not a part of the C# standard. They are defined in the BCL.)
Playing around with lifted operators on nullable TimeSpan
and DateTime
values, I wrote the following code. Note that the framework offers different operations on TimeSpan
and DateTime
.
There's one symmetrical (and commutative) addition where you take in two TimeSpan
and return the sum TimeSpan
. The "inverse" of this addition is the subtraction of two TimeSpan
yielding a TimeSpan
.
Then there's another kind of addition, asymmetrical, where you take one DateTime
(left operand) and one TimeSpan
(right operand) to produce a DateTime
. Because of the asymmetry of this operation, it has two "kinds" of inverses: One where you subtract two DateTime
from each other to get the TimeSpan
difference, and one where you have one DateTime
and subtract from it one TimeSpan
to produce a result DateTime
.
static void Main()
{
DateTime? n_dt = new DateTime(2012, 12, 25);
TimeSpan? n_ts = TimeSpan.FromDays(62.0);
var a = n_dt + n_ts; // OK
var b = n_ts + n_ts; // OK
var c = null + n_dt; // OK, string concatenation! Type of expression is String
var d = null + n_ts; // OK, compiler prefers TS+TS, not DT+TS
var e = n_dt + null; // OK, DT+TS
var f = n_ts + null; // OK, TS+TS
var g = null + null; // error, type of expression is undetermined
var h = n_dt - n_dt; // OK
var i = n_dt - n_ts; // OK
var j = n_ts - n_ts; // OK
var k = null - n_dt; // OK, DT-DT
var l = null - n_ts; // compiler prefers TS-TS, not DT-TS
var m = n_dt - null; // error, compiler won't choose between DT-DT amd DT-TS, type of expression is undetermined
var n = n_ts - null; // OK, TS-TS
var o = null - null; // OK, integer subtraction! Type of expression is Nullable<Int32>
// illegal:
//var p = n_dt + n_dt;
//var q = n_ts + n_dt;
//var r = n_ts - n_dt;
}
Some questions arise naturally.
It's a bit strange that o
is allowed and gives an int?
(why not a long?
by the way?) while g
is disallowed. Is this in the spec? Also, it's a little strange that the "impossible" c
is resolved by string concatenation. Apparently the compiler decides that the null
in c
is a (string)null
. Adding an expression of explicit type object
to a DateTime
, on the other hand, will not compile.
But my main question is: Why can the compiler choose an overload for d
and l
, but with m
it complains about ambiguity?
The reason seems to be that with m
, the two possible operations are defined inside the same type, namely System.DateTime
. There's no way to choose between them.
On the other hand, with d
and l
, one operation is defined in System.TimeSpan
, and the other one is defined in System.DateTime
. But in the lines of d
and l
, we see TimeSpan
, but there's no mention of any DateTime
type what so ever in the assignments of d
and l
. It seems like the compiler then only searches through operators defined in the type System.TimeSpan
, and forgets to search through user-defined operators defined in all other types (which would by the way be very many types to search). In that way, during the resolution of d
and l
, the compiler never discovers the operators defined inside the DateTime
type.