5

(Określenie "zdefiniowane przez użytkownika" w tytule odnosi się do faktu, że dodawanie i odejmowanie TimeSpan i DateTime nie jest częścią standardu C#. zdefiniowany w BCL.)Ciekawa rozdzielczość przeciążania, gdy używany jest pusty znak null z operatorami zdefiniowanymi przez użytkownika

Zabawy z podniesionymi operatorami na wartości zerowe TimeSpan i DateTime wartości, napisałem następujący kod. Zauważ, że framework oferuje różne operacje na TimeSpan i DateTime.

Istnieje jeden symetryczny (i komutatywny) dodatek, w którym przyjmujemy dwa TimeSpan i zwracamy sumę TimeSpan. "Odwrotność" tego dodawania jest odjęciem dwóch TimeSpan uzyskując TimeSpan.

Potem jest inny rodzaj Ponadto, asymetryczny, gdzie można podjąć jedną DateTime (lewy operand) i jeden TimeSpan (prawy operand) w celu wytworzenia DateTime. Ze względu na asymetrię tej operacji, ma dwa „rodzaje” o odwrotności: Jedna gdzie odjąć dwa DateTime od siebie, aby uzyskać różnicę TimeSpan i jedną gdzie trzeba DateTime i odjąć od niej jeden TimeSpan pozwoliło uzyskać 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; 
} 

Niektóre pytania pojawiają się naturalnie.

To trochę dziwne, że o wolno i daje int? (dlaczego nie long? w drodze?) Podczas g jest niedozwolone. Czy to w specyfikacji? Ponadto, jest trochę dziwne, że "niemożliwe" c jest rozwiązywany przez konkatenację ciągów. Wygląda na to, że kompilator zdecydował, że null w c jest (string)null. Z drugiej strony dodanie wyrażenia typu jawnego: object do DateTime nie zostanie skompilowane.

Ale moje główne pytanie brzmi: Dlaczego kompilator wybrać przeciążenie dla d i l, ale m to narzeka dwuznaczności?

+3

Do tej pory najdziwniejszą rzeczą tutaj jest var o = null - null; –

+0

'c' używa konkatenacji ciągów znaków, ponieważ operatory języków są preferowane w stosunku do operatorów zdefiniowanych przez użytkownika, a wyrażenie to pasuje do operatora konkatenacji łańcuchów operatora' operator + (ciąg, obiekt) '. To oczywiście podnosi kwestię 'd' chociaż, jak przez tę logikę, która powinna również rozwiązać ciąg konkatenacji. – Servy

+0

@DaveBish Zgoda. Zakładam, że byłoby to nieuchwytne między int/long – Servy

Odpowiedz

0

Przyczyną wydaje się być to, że z m, dwa możliwe operacje są zdefiniowane wewnątrz tego samego typu, a mianowicie System.DateTime. Nie ma możliwości wyboru między nimi.

Z drugiej strony, z d i l, jedna operacja jest zdefiniowana w System.TimeSpan, a drugi jest zdefiniowany w System.DateTime. Ale w liniach d i l widzimy TimeSpan, ale nie ma żadnej wzmianki o typie , co zawsze w zadaniach d i . Wygląda na to, że kompilator przeszukuje tylko operatory zdefiniowane w typie System.TimeSpan i zapomina przeszukiwać zdefiniowane przez użytkownika operatory zdefiniowane we wszystkich innych typach (które przy okazji byłyby bardzo wieloma typami wyszukiwania). W ten sposób podczas rozwiązywania d i l kompilator nigdy nie odkryje operatorów zdefiniowanych w typie DateTime.

+1

Zobacz punkt 7.3.4 specyfikacji C#, ponieważ wydaje się to istotne i dla potwierdzenia tej odpowiedzi. – Servy

+0

"Na marginesie". – phoog