573 Stimmen

Merkwürdiges Verhalten des Null-Koaleszenz-Operators bei der benutzerdefinierten impliziten Konvertierung

Hinweis: Dieses Problem scheint in der folgenden Version behoben worden zu sein Roslyn

Diese Frage stellte sich beim Verfassen meiner Antwort auf celui-ci die über die Assoziativität der Nullkoalitionsoperator .

Zur Erinnerung: Die Idee des Null-Koalitionsoperators ist, dass ein Ausdruck der Form

x ?? y

wertet zunächst aus x , dann:

  • Wenn der Wert von x null ist, y ausgewertet wird und das ist das Endergebnis des Ausdrucks
  • Wenn der Wert von x nicht null ist, y es pas bewertet, und der Wert von x ist das Endergebnis des Ausdrucks nach einer Umwandlung in den Kompilierzeittyp von y falls erforderlich

Jetzt in der Regel keine Konvertierung erforderlich ist oder es sich nur um eine Konvertierung von einem nullbaren Typ in einen nicht-nullbaren Typ handelt - in der Regel sind die Typen gleich, oder nur von (sagen wir) int? a int . Aber Sie kann Ihre eigenen impliziten Konvertierungsoperatoren erstellen, die dann bei Bedarf verwendet werden.

Für den einfachen Fall von x ?? y Ich habe kein merkwürdiges Verhalten festgestellt. Allerdings, mit (x ?? y) ?? z Ich sehe ein verwirrendes Verhalten.

Hier ist ein kurzes, aber vollständiges Testprogramm - die Ergebnisse finden Sie in den Kommentaren:

using System;

public struct A
{
    public static implicit operator B(A input)
    {
        Console.WriteLine("A to B");
        return new B();
    }

    public static implicit operator C(A input)
    {
        Console.WriteLine("A to C");
        return new C();
    }
}

public struct B
{
    public static implicit operator C(B input)
    {
        Console.WriteLine("B to C");
        return new C();
    }
}

public struct C {}

class Test
{
    static void Main()
    {
        A? x = new A();
        B? y = new B();
        C? z = new C();
        C zNotNull = new C();

        Console.WriteLine("First case");
        // This prints
        // A to B
        // A to B
        // B to C
        C? first = (x ?? y) ?? z;

        Console.WriteLine("Second case");
        // This prints
        // A to B
        // B to C
        var tmp = x ?? y;
        C? second = tmp ?? z;

        Console.WriteLine("Third case");
        // This prints
        // A to B
        // B to C
        C? third = (x ?? y) ?? zNotNull;
    }
}

Wir haben also drei benutzerdefinierte Wertetypen, A , B y C mit Umwandlungen von A nach B, A nach C und B nach C.

Ich kann sowohl den zweiten als auch den dritten Fall verstehen... aber warum Gibt es im ersten Fall eine zusätzliche Umwandlung von A nach B? Insbesondere würde ich realmente Ich habe erwartet, dass der erste Fall und der zweite Fall dasselbe sind - es geht ja nur darum, einen Ausdruck in eine lokale Variable zu extrahieren.

Weiß jemand, was hier los ist? Ich bin extrem zögerlich zu schreien "Bug", wenn es um den C#-Compiler kommt, aber ich bin ratlos, was los ist...

EDIT: Okay, hier ist ein böseres Beispiel für das, was vor sich geht, dank der Antwort des Konfigurators, was mir einen weiteren Grund gibt, zu glauben, dass es ein Fehler ist. EDIT: Das Beispiel braucht jetzt nicht einmal mehr zwei Null-Koalitionsoperatoren...

using System;

public struct A
{
    public static implicit operator int(A input)
    {
        Console.WriteLine("A to int");
        return 10;
    }
}

class Test
{
    static A? Foo()
    {
        Console.WriteLine("Foo() called");
        return new A();
    }

    static void Main()
    {
        int? y = 10;

        int? result = Foo() ?? y;
    }
}

Das Ergebnis ist:

Foo() called
Foo() called
A to int

Die Tatsache, dass Foo() hier zweimal genannt wird, ist für mich sehr überraschend - ich kann keinen Grund erkennen, warum der Ausdruck bewertet zweimal.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X