2009-08-04 5 views
18

Ich habe festgestellt, dass einige .NET-Strukturen mit null verglichen werden können. Zum Beispiel:Warum können TimeSpan und Guid Structs mit null verglichen werden?

TimeSpan y = new TimeSpan(); 
     if (y == null) 
      return; 

wird gut kompilieren (das gleiche mit der Guid-Struktur).
Jetzt weiß ich, dass Stucts Werttyp sind und dass der obige Code nicht kompilieren sollte, es sei denn, es gibt eine Überladung von operator ==, die ein Objekt nimmt. Soweit ich das beurteilen konnte, gibt es das nicht.
Ich habe die Klasse mit Reflector und auch in der Dokumentation auf MSDN angesehen.
Die beiden die folgenden Schnittstellen implementieren:

IComparable, IComparable<T>, IEquatable<T> 

aber versuchen, die gleichen Schnittstellen implment schien nicht zu helfen:

struct XX : IComparable, IComparable<XX>, IEquatable<XX> { 
    public int CompareTo(Object obj) { 
     return 0; 
    } 
    public int CompareTo (XX other){ 
     return 0; 
    } 
    public bool Equals (XX other){ 
     return false; 
    } 
    public override bool Equals(object value){ 
     return false; 
    } 
    public static int Compare(XX t1, XX t2){ 
     return 0; 
    } 
} 

Ich verwende: 2.0 Visual Studio .NET 2005.

Hat jemand eine Idee, was ist der Grund dafür? Ich versuche nur, ein besseres Verständnis zu bekommen. Dies ist kein Problem, da ich weiß, dass ich structs nicht mit null vergleichen sollte.

+0

"Ich verwende: .NET 2.0 Visual Studio 2005." - Ist das eine Warnung? IIRC, sollte es im Jahr 2005 tun (aber nicht 2008, siehe unten) –

+0

Keine Warnung Für den Zeitraum gibt es einen Fehler für XX: Fehler CS0019: Operator ‚==‘ kann nicht auf Operanden vom Typ angewandt werden ‚TransControl.TraceDisplay.XX‘ und '' – dtroy

+0

mögliches Duplikat von [Wie kann ein Objekt nicht mit null verglichen werden?] (http://stackoverflow.com/questions/648115/how-can-an-object-not-be-compared-to-null) – nawfal

Antwort

13

Es ist der Operator ==.

Die TimeSpan Klasse hat eine Überlastung des Gleichheitsoperator:

public static bool operator ==(DateTime d1, DateTime d2) 
{ 
    return (t1._ticks == t2._ticks); 
} 

Das ist an sich nicht möglich mit null vergleichen macht, aber ...

Mit der Ankunft von Nullable-Typen, Jede Struktur ist implizit in den Nullable-Typ umwandelbar. Wenn Sie also etwas wie

sehen

Sie nicht sehen, dass dies geschieht: (implizite Zuweisung)

TimeSpan y = new TimeSpan(); 
if ((Nullable<TimeSpan>)y == (Nullable<TimeSpan>)null) 
    return; 

Null bekommt die implizite Konvertierung, aber nicht alle System.Object Objekte tun:

TimeSpan y = new TimeSpan(); 
object o = null; 
if (y == o) //compiler error 
    return; 

Okay, aber der Gleichheitsoperator akzeptiert keine nullbaren Argumente, oder?

Nun, msdn ist hier von Hilfe, die besagt:

Die vordefinierten unären und binären Operatoren und alle benutzerdefinierten Operatoren, die für Werttypen existieren auch von Nullable-Typen verwendet werden können. Diese Operatoren erzeugen einen Nullwert , wenn [jeder der] Operanden null ist; Andernfalls verwendet der Operator den enthaltenen Wert , um das Ergebnis zu berechnen.

So erhalten Sie effektiv eine Nullable-Implementierung für jeden Betreiber kostenlos, mit einem fest definierten Verhalten. Der oben erwähnte "enthaltene Wert" ist der tatsächliche Wert, den der nicht nullbare Operator zurückgeben würde.

+0

Sie haben Recht! es ist der Operator ==! Wenn ich es zu meiner benutzerdefinierten Struktur hinzufüge, kann ich jetzt mit null vergleichen! – dtroy

+0

Ja. Vielleicht hätte ich diesen Satz am Anfang meines Posts für Klarheit hinzufügen sollen :) –

7

Dieser Fall wird für Generics in Abschnitt 7.9.6 der C# -Sprachspezifikation behandelt.

Das Konstrukt x == null ist zulässig, obwohl T einen Werttyp darstellen könnte, und das Ergebnis wird einfach als falsch definiert, wenn T ein Werttyp ist.

Ich durchforstete die Spezifikation für ein bisschen und konnte keine allgemeinere Regel finden. Jon's answer zeigt an, dass es ein NULL-fähiges Aktionsproblem ist.

Diese Regel (oder eine ähnliche Variante) scheint hier angewendet zu werden. Wenn Sie die reflektierte Ausgabe genau betrachten, werden Sie feststellen, dass der Vergleich nicht vorhanden ist. Der C# -Compiler optimiert diesen Vergleich offensichtlich und ersetzt ihn durch false.

Zum Beispiel, wenn Sie die folgenden

var x = new TimeSpan(); 
var y = x == null; 
Console.WriteLine(x); 

Dann decompile es geben Sie werden sehen, die folgenden

var x = new TimeSpan(); 
var y = false; 
Console.WriteLine(x); 
+2

Tatsächlich sollte der Compiler hier eine Warnung ausgeben, wenn er den Vergleich ersetzt. Zumindest unter anderen Umständen. Geheimnisvoll. –

+0

Es ist auch interessant, dass, wenn Sie es in einen Vergleich einfügen ... if (x == null) Sie erhalten eine Warnung für einen int-Typ, aber nicht für einen Guid-Typ. Vielleicht gibt es einen geheimen Weg, um einen Guid aufzulösen. :) –

+0

Und warum funktioniert es nicht auf benutzerdefinierten Strukturen? –

3

Ich fand es :)

Im Folgenden eine gibt Warnung:

int i = 0; 
if (i == null) 
// ^^ Warning: The result of the expression is always 'false' since a value of 
//    type 'int' is never equal to 'null' of type 'int?' 

Der Compiler gibt gerade die korrekte Warnung aus, dass die von Ihnen eingegebene null für den Vergleich in den Typ TimeSpan? konvertiert wurde.

Edit: Der entsprechende Abschnitt in der Spezifikation ist §13.7.1 besagt, dass null implizit in jeden Nullable Type umgewandelt werden, und (das sehr schwer zu lesen) Abschnitt §13.7.2 Angabe Wert Typ T kann implizit sein konvertiert zu T?.

Was ich ursprünglich schrieb:

Was auch immer etwas in der C# spec ist passiert, weil wie JaredPar sagt es false einfach kompiliert.

Beachten Sie, dass dies nicht kompiliert:

TimeSpan ts = new TimeSpan(); 
object o = null; 
if (ts == o) // error, Operator '==' cannot be applied to operands of type 'System.TimeSpan' and 'object' 
    ... 
7

Dieses Problem effektiv eingeführt wurde, als Nullable Types enthalten waren. Es gibt eine implizite Konvertierung von TimeSpan zu TimeSpan?, und es gibt einen Vergleich zwischen TimeSpan? und dem Nullwert dieses Typs.

Der Compiler gibt eine Warnung für einige Arten, die macht es klarer, was es zu tun versucht:

int x = 10; 
if (x == null) 
{ 
    Console.WriteLine(); 
} 

gibt diese Warnung:

Test.cs(9,13): warning CS0472: The result of the expression is always 'false' 
     since a value of type 'int' is never equal to 'null' of type 'int?' 

I Marc GRA glauben und ich die Umstände ausgearbeitet unter dem die Warnung einmal gegeben wird ... es ist eine Schande, dass es nicht konsequent ist.

+0

Darn, du schlägst mich dazu; -p Für jede benutzerdefinierte Struktur (war es mit Equals? ==?) Wird es ** still (ohne Warnung) bemerken, dass es überflüssig ist. IIRC änderte es zwischen C# 2.0 und C# 3.0; Möchten Sie C# 4.0 ausprobieren? Es könnte wieder repariert werden ... –

+0

@Marc, habe ich versucht, die OP-Repro auf VS2010 Beta1 und es Repros. Habe nicht viele andere Fälle versucht – JaredPar

+0

@JaredPar - ja, als ich das connect-Problem, das ich gepostet habe, gefunden habe, sagte Ed Maurer, dass er es nicht reparieren würde. Entschuldigung, mein Kommentar von 2010 vordatierte mich, den Artikel zu finden ;-p –

Verwandte Themen