2012-06-18 12 views
24

Ich bin neugierig zu wissen, warum der C# -Compiler mir nur eine Fehlermeldung für die zweite if-Anweisung gibt.Operator ">" kann nicht auf "ulong" und "int" angewendet werden

enum Permissions : ulong 
{ 
    ViewListItems = 1L, 
} 

public void Method() 
{ 
    int mask = 138612833; 
    int compare = 32; 

    if (mask > 0 & (ulong)Permissions.ViewListItems > 32) 
    { 
     //Works 
    } 

    if (mask > 0 & (ulong)Permissions.ViewListItems > compare) 
    { 
     //Operator '>' cannot be applied to operands of type 'ulong' and 'int' 
    } 
} 
+1

Der Grund für diesen Fehler ist klar. Im zweiten Beispiel versuchen Sie, eine ungültige Operation mit einer Ganzzahl auszuführen. Im ersten Beispiel wird "32" als "ulong" behandelt. Du solltest "Maske" tatsächlich zu einem "ulong" machen. –

+0

@Ramhound Entweder das (vergleiche 'ein' ulong'), oder 'compare' eine' const' Variable, also 'const int compare = 32;'. Constant Expressions werden zur Kompilierungszeit automatisch von "int" in "ulong" konvertiert, wenn sie nicht negativ sind, was unter dem Namen _Implicit constant expression conversions_ steht. –

Antwort

30

Ich habe experimentiert mit ILSpy, um die Ausgabe zu untersuchen, und das ist, was ich entdeckt habe.

Offensichtlich in Ihrem zweiten Fall ist dies ein Fehler - Sie können eine ulong und eine int nicht vergleichen, da es keinen Typ gibt, den Sie beide erzwingen können. A ulong könnte für eine long zu groß sein, und eine int könnte negativ sein.

In Ihrem ersten Fall ist der Compiler jedoch clever. Es realisiert, dass const 1> const 32 ist nie wahr, und enthält Ihre if Anweisung in der kompilierten Ausgabe überhaupt nicht. (Es sollte eine Warnung für nicht erreichbaren Code geben.) Es ist das gleiche, wenn Sie eine const int anstelle eines Literals definieren und verwenden, oder sogar wenn Sie das Literal explizit umwandeln (d. H. (int)32).

Aber dann ist nicht der Compiler erfolgreich eine ulong mit einem int zu vergleichen, was wir gerade gesagt, unmöglich war?

Offenbar nicht. Also was ist los?

Versuchen Sie stattdessen, etwas in den folgenden Zeilen zu tun. (Unter-Eingang und Schreiben Ausgang so dass der Compiler hat nichts kompilieren entfernt.)

const int thirtytwo = 32; 
static void Main(string[] args) 
{ 
    ulong x = ulong.Parse(Console.ReadLine()); 
    bool gt = x > thirtytwo; 
    Console.WriteLine(gt); 
} 

Dies kompiliert, obwohl die ulong ist eine Variable, und obwohl das Ergebnis wird bei der Kompilierung nicht bekannt. Werfen Sie einen Blick auf die Ausgabe in ILSpy:

private static void Main(string[] args) 
{ 
    ulong x = ulong.Parse(Console.ReadLine()); 
    bool gt = x > 32uL;  /* Oh look, a ulong. */ 
    Console.WriteLine(gt); 
} 

So ist der Compiler in der Tat ist Ihre const int als eine ulong behandeln. Wenn Sie thirtytwo = -1 machen, kann der Code nicht kompiliert werden, obwohl wir dann wissen, dass gtimmer sein wird. Der Compiler selbst kann ulong nicht mit int vergleichen.

Beachten Sie auch, dass, wenn Sie x eine long anstelle eines ulong machen, der Compiler 32L erzeugt anstatt 32 als eine ganze Zahl, auch wenn dies nicht der Fall haben zu. (Sie können einen int und long zur Laufzeit vergleichen.)

Dies weist auf die Compiler nicht 32 als ulong im ersten Fall behandeln, weil es zu hat, nur weil es die Art der x bieten kann . Es spart der Laufzeit, die Konstante zu erzwingen, und das ist nur ein Bonus, wenn der Zwang eigentlich nicht möglich sein sollte.

+1

Dies war eine sehr interessante Lektüre, Requisiten für die Forschung. – Amicable

+1

@Amicable - Requisiten zu _you_ für eine interessante Frage, die mich dazu brachte, es zu erforschen :) – Rawling

+0

Der C# -Compiler stimmt hier vollständig mit der C# -Sprachspezifikation überein. Wenn ein Ausdruck vom Typ "int" eine Kompilierzeitkonstante ist, existiert eine [implizite konstante Ausdruckskonvertierung] (http://msdn.microsoft.com/en-us/library/aa691286.aspx) von "int" zu "ulong" _ vorausgesetzt, dass der Wert innerhalb des Bereichs von 'ulong' liegt, dh nicht negativ, was der C# -Compiler mit einem konstanten Ausdruck sehen kann. '32' ist ein konstanter Ausdruck vom Typ' int'. Wenn ein Ausdruck vom Typ 'int' _nicht_ eine Kompilierzeitkonstante ist, existiert keine implizite Umwandlung in 'ulong'. Beispiel 'vergleichen'. –

21

Es ist nicht die CLR, die diese Fehlermeldung gibt, es ist der Compiler.

In Ihrem ersten Beispiel des Compiler behandelt 32 als ulong (oder einen Typ, der zu ulong zB uint implizit konvertierbar ist), während in Ihrem zweiten Beispiel, Sie haben ausdrücklich auf die Art als int erklärt. Es gibt keine Überlastung des > Operators, der eine ulong und eine int akzeptiert, und daher erhalten Sie einen Compilerfehler.

+7

Die Integer-Literalsseite (http://msdn.microsoft.com/en-us/library/aa664674.aspx) sagt "Wenn das Literal kein Suffix hat, hat es den ersten dieser Typen, in dem sein Wert dargestellt werden kann: Int, Uint, lang, ulong. " Plus mein Intellisense (Tooltip) sagt das '32' ist ein 'int'. Aber das scheint zu sein, was passiert ... – Rawling

+1

@Rawling Sehr wahr, ich kann drei mögliche Gründe sehen (ich wage zu sagen, es gibt noch viel mehr): # 1 vielleicht wegen unterschiedlicher Interpretationen von "Wert kann dargestellt werden", dh der Compiler weiß, dass 32 nicht ein int sein kann, da der Code nicht kompilieren würde und versucht als nächstes was folgt. Intellisense bewertet offensichtlich nicht den gesamten Ausdruck, um den Typ zu berechnen, int ist im Allgemeinen korrekt, also warum Rechenzeit für einen Kantenfall opfern. # 2 Die Dokumentation für Integer-Literale ist veraltet. Oder # 3 ist ein Compiler Bug. –

+0

@Rawling - Es ist kein Fehler, es funktioniert genau so, wie es sollte. Das Literal "32" wird als "Lang" behandelt. Sie zitieren sogar die Aussage, wo es heißt, dass es als "long" behandelt wird. –

3

Die Antworten von Rich.Kelly und Rawling sind korrekt, weshalb Sie sie nicht direkt vergleichen können. Sie können die ToUInt64-Methode der Convert-Klasse verwenden, um den int zu fördern.

if (mask > 0 & (ulong)Permissions.ViewListItems > Convert.ToUInt64(compare)) 
{ 
} 
Verwandte Themen