2016-02-28 9 views
5

Warum ist eine explizite Konvertierung von double zu Foo möglich, obwohl Foo nur eine explizite Konvertierung von int zu Foo definiert?Warum konvertiert der Compiler implizit ein double in int, wenn ein expliziter benutzerdefinierter Operator von int nach Foo existiert?

Warum wird in meinem Fall double implizit in eine int konvertiert?

using System; 

class Program 
{ 
    static void Main(string[] args) 
    { 
     double doub = 15.7; 
     Foo foo = (Foo)doub; 
     Console.WriteLine(foo.value); //writes "15" 
    } 
} 

struct Foo 
{ 
    public int value; 
    public static explicit operator Foo(int val) //no matter if implicit 
    { 
     return new Foo { value = val }; 
    } 
} 
+0

das ist explizit. Wenn Sie versuchen, impliziten Operator Foo (int val) 'Sie werden sehen, dass Sie nicht schreiben können' Foo foo = double; '... –

+0

@ M.kazemAkhgary Dieser Code verursacht Fehler. http://ideone.com/ttBLLX Aber mit 'öffentlichen statischen impliziten Operator Foo (int val)' 'Foo a = (Foo) double;' funktioniert – Alex78191

+4

Die gegebenen Antworten sind gut. Weitere Informationen finden Sie unter https://blogs.msdn.microsoft.com/ericlippert/2007/04/16/chained-user-defined-explicit-conversions-in-c/ und https://blogs.msdn.microsoft.com/ericlippert/2007/04/18/verkettete-benutzerdefiniert-explizite-conversions-in-c-teil-two/und https://blogs.msdn.microsoft.com/ericlippert/2007/04/20/chained- user-defined-explicit-conversions-in-c-teil-three/ –

Antwort

1

Der entsprechende Abschnitt der C# Spezifikation ist

Der Abschnitt ist langwierig, aber es destillieren dazu: Sie eine explizite Konvertierung mit (Foo)doub definieren, und da Wenn Sie eine explizite Konvertierung verwenden, wählt der Compiler die spezifischsten verfügbaren Conversions aus, einschließlich Conversions, die ausschließlich explizit sind, um die Zwischenschritte bei der Conversion durchzuführen Pfad (wenn sie verfügbar sind).

Wenn U enthält genau einen benutzerdefinierten Umwandlungsoperator die von SX zu TX umwandelt, dann ist dies die spezifische Umwandlung Operator. Wenn kein solcher Operator vorhanden ist oder wenn mehr als ein solcher Operator vorhanden ist, ist die Konvertierung nicht eindeutig und es tritt ein Kompilierungszeitfehler auf. Andernfalls wird die benutzerdefinierte Konvertierung angewendet:

Wenn S nicht SX ist, wird eine explizite Standardkonvertierung von S nach SX durchgeführt.
Der spezifischste benutzerdefinierte Konvertierungsoperator wird aufgerufen, um von SX in TX zu konvertieren.
Wenn TX nicht T ist, wird eine explizite Standardkonvertierung von TX nach T durchgeführt.

(Hervorhebung von mir)

Hier S ist die Quelle Typ und T ist der Zieltyp, während SX ist der spezifische Quelle Typ in dem expliziten Betreiber des Zieltypen definiert (das heißt, Foo) und TX ist der spezifischste Zieltyp, der in den expliziten Operatoren T definiert ist.

Hier existiert eine explizite Umwandlung von double zu int und es daher als die am besten geeignete gewählt wird aus der Quelle Argumenttyp (double -- S) mit dem Eingang Argumenttyp des expliziten Operator (int -- SX) zu konvertieren. Es muss nicht explizit sein, da Sie bereits ausdrücklich die Konvertierung in Foo vorgenommen haben.

Dies funktioniert nur, weil es keine zweideutigen Alternativen gibt. Wenn Sie Foo definiert haben:

struct Foo 
{ 
    public int value; 
    public uint uvalue; 
    public static explicit operator Foo(int val) 
    { 
     return new Foo { value = val }; 
    } 


    public static explicit operator Foo(uint val) 
    { 
     return new Foo { uvalue = val }; 
    } 
} 

dies zum Beispiel erzeugt ein Fehler bei der Kompilierung (mit dem gleichen Main) von:

Fehler 1 Mehrdeutige benutzerdefinierte Konvertierungen ‚Foo.expliziten Operator Foo (uint)‘und 'Foo.explicit Operator Foo (int) double 'auf '

Again' Foo', wenn aus der Umwandlung', das durch das Buch ist (gemäß dem ersten Absatz oben zitierten) seit Die Menge der verfügbaren Konvertierungen U enthält jetzt zwei gleichwertige explizite Konvertierungen und der Compiler kann nicht entscheiden, ob Sie die int Konvertierung oder die uint Konvertierung aufrufen möchten. Im ersten Beispiel gibt es nur einmal Auswahl, und es ist klar, so dass der Compiler es nimmt.

8

Ich glaube, das ist in Abschnitt 6.4.3 der C# 5 Language Specification festgelegt.

Eine benutzerdefinierte explizite Umwandlung besteht aus einer Standard-optional expliziten Umwandlung, gefolgt von der Ausführung von :

Ein ersten Hinweis wird in Abschnitt 6.2.8 User Defined explizite Konvertierungen (emphasis Mine) gegeben ein benutzerdefinierter impliziter oder expliziter Konvertierungsoperator, von einer anderen optional Standard expliziten Konvertierung gefolgt

Beachten Sie, wie nicht o ne aber möglicherweise drei Konvertierungen auftreten.

Nun, zu wissen, was ein ‚Standard explizite Konvertierung‘ ist, müssen wir in Abschnitt sehen 6.2.3 Standard explicit conversions:

Die Standard explizite Konvertierungen sind alle Standard-implizite Konvertierungen und die Teilmenge der expliziten Conversions, für die ein entgegengesetzte Standard implizite Konvertierung existiert. Mit anderen Worten, wenn eine Standard-implizite Umwandlung von einem B-Typ-A auf eine Art existiert, dann eine Standard explizite Konvertierung besteht vom Typ A zu Typ B und vom Typ B A.

Und im Rückblick zu geben im Abschnitt 6.3.1 Standard implicit conversions können wir sehen, dass dies ein Teil davon ist:

  • Implizite numerische Konvertierungen (§6.1.2)

Mit anderen Worten: Eine explizite numerische Konvertierung (wie double -> int) kann vor der benutzerdefinierten Konvertierung angewendet werden.

Wenn wir nun einen Blick auf 6.4.3 Evaluation of user-defined conversions wir folgende (Hervorhebung von mir) sehen:

Erstens, wenn erforderlich, eine Standardkonvertierung von der Quelle Typ des Operanden-Typ des userdefined oder angehoben Umwandlungsoperators durchführt

Als nächstes wird der benutzerdefinierte oder aufgehobene Konvertierungsoperator aufgerufen, um die Konvertierung durchzuführen.

Schließlich, falls erforderlich, eine Standardkonvertierung vom Ergebnistyp des benutzerdefinierten oder aufgehobenen Konvertierungsoperators zum Zieltyp durchführen.

Was genau passiert in Ihrem Szenario.

+2

Tatsächlich können Sie bis zu * vier * Conversions erzielen, indem Sie Nullable-Conversions vornehmen: https://blogs.msdn.microsoft.com/ericlippert/2007/04/16/chained-user-defined-explicit-conversions-in-c/ –

+0

Ah, ich war mir nicht sicher, ob das als eine Konvertierung unter Berücksichtigung der C# LS es als "* Für die Zwecke dieses Schrittes, wenn entweder Der Quell- oder Zieltyp ist ein NULL-Typ, dessen zugrunde liegender Typ stattdessen verwendet wird * ". Gut zu wissen! –