Freigeben über


Rechtsverschiebungs-Operator ohne Vorzeichen

Anmerkung

Dieser Artikel ist eine Featurespezifikation. Die Spezifikation dient als Designdokument für das Feature. Es enthält vorgeschlagene Spezifikationsänderungen sowie Informationen, die während des Entwurfs und der Entwicklung des Features erforderlich sind. Diese Artikel werden veröffentlicht, bis die vorgeschlagenen Spezifikationsänderungen abgeschlossen und in die aktuelle ECMA-Spezifikation aufgenommen werden.

Es kann einige Abweichungen zwischen der Featurespezifikation und der abgeschlossenen Implementierung geben. Diese Unterschiede werden in den entsprechenden Hinweisen zum Language Design Meeting (LDM) erfasst.

Weitere Informationen zum Prozess für die Aufnahme von Funktions-Speclets in den C#-Sprachstandard finden Sie im Artikel zu den Spezifikationen.

Champion Issue: https://github.com/dotnet/csharplang/issues/4682

Zusammenfassung

Ein vorzeichenloser Rechtsverschiebungs-Operator wird von C# als integrierter Operator (für primitive Integraltypen) und als benutzerdefinierter Operator unterstützt.

Motivation

Bei der Arbeit mit vorzeichenbehafteten Integralwerten ist es nicht ungewöhnlich, dass Sie Bits nach rechts verschieben müssen, ohne das Bit hoher Ordnung bei jeder Verschiebung zu replizieren. Während dies bei primitiven Integraltypen mit einem regulären Verschiebungs-Operator erreicht werden kann, ist vor dem Verschiebungsvorgang ein Cast in einen vorzeichenlosen Typ und danach ein Cast-Back erforderlich. Im Zusammenhang mit den generischen mathematischen Schnittstellen, die die Bibliotheken planen, ist dies potenziell problematischer, da der Typ nicht unbedingt ein vorzeichenloses Gegenstück haben muss, das definiert oder dem generischen mathematischen Code im Voraus bekannt ist, ein Algorithmus jedoch auf die Fähigkeit angewiesen sein könnte, einen vorzeichenlosen Right-Shift-Operator durchzuführen.

Detailliertes Design

Operatoren und Trennzeichen

Abschnitt §6.4.6 wird geändert, um den >>>-Operator aufzunehmen – den Rechtsverschiebungs-Operator ohne Vorzeichen:

unsigned_right_shift
    : '>>>'
    ;

unsigned_right_shift_assignment
    : '>>>='
    ;

Zwischen den Token in den Produktionen unsigned_right_shift und unsigned_right_shift_assignment sind keine Zeichen (auch keine Leerzeichen) zugelassen. Diese Produktionen werden speziell behandelt, um die korrekte Behandlung von typ_parameter_list zu ermöglichen.

Schiebeoperatoren

Abschnitt §12.11 wird geändert, um den >>>-Operator (den Rechtsverschiebungs-Operator ohne Vorzeichen) aufzunehmen:

Die Operatoren <<, >> und >>> werden zum Ausführen von Bitverschiebungsvorgängen verwendet.

shift_expression
    : additive_expression
    | shift_expression '<<' additive_expression
    | shift_expression right_shift additive_expression
    | shift_expression unsigned_right_shift additive_expression
    ;

Für einen Vorgang der Form x << count, x >> count oder x >>> count wird die Überladungsauflösung für binäre Operatoren (§12.4.5) angewendet, um eine bestimmte Implementierung des Operators auszuwählen. Die Operanden werden in die Parametertypen des ausgewählten Operators konvertiert, und der Typ des Ergebnisses ist der Rückgabetyp des Operators.

Die vordefinierten vorzeichenlosen Verschiebungsoperatoren unterstützen dieselbe Reihe von Signaturen, die vordefinierte vorzeichenbehaftete Verschiebungsoperatoren in der aktuellen Implementierung unterstützen.

  • Nach rechts verschieben:

    int operator >>>(int x, int count);
    uint operator >>>(uint x, int count);
    long operator >>>(long x, int count);
    ulong operator >>>(ulong x, int count);
    nint operator >>>(nint x, int count);
    nuint operator >>>(nuint x, int count);
    

    Der >>>-Operator verschiebt x um eine Anzahl von Bits nach rechts, die wie unten beschrieben berechnet wird.

    Die niederwertigen Bits von x werden verworfen. Die verbleibenden Bits werden nach rechts verschoben und die höherwertigen leeren Bitpositionen werden auf Null festgelegt.

Für die vordefinierten Operatoren wird die Anzahl der zu verschiebenden Bits wie folgt berechnet:

  • Wenn der Typ von xint oder uint ist, wird die Anzahl der Verschiebungen durch die niederwertigen fünf Bits von count bestimmt. Mit anderen Worten, die Anzahl der Verschiebungen wird aus count & 0x1F berechnet.
  • Wenn der Typ von xlong oder ulong ist, wird die Anzahl der Verschiebungen durch die niederwertigen sechs Bits von count bestimmt. Mit anderen Worten, die Anzahl der Verschiebungen wird aus count & 0x3F berechnet.

Wenn die resultierende Schichtanzahl null ist, geben die Schichtoperatoren einfach den Wert von xzurück.

Verschiebungsvorgänge führen nie zu Überläufen und liefern in checked- und unchecked-Kontexten die gleichen Ergebnisse.

Assignment operators (Zuweisungsoperatoren)

Abschnitt §12.21 wird geändert, um unsigned_right_shift_assignment wie folgt aufzunehmen:

assignment_operator
    : '='
    | '+='
    | '-='
    | '*='
    | '/='
    | '%='
    | '&='
    | '|='
    | '^='
    | '<<='
    | right_shift_assignment
    | unsigned_right_shift_assignment
    ;

Ganzzahlige Typen

Der Abschnitt über die integralen Typen §8.3.6 wird angepasst, um Informationen über den >>>-Operator zu integrieren. Der entsprechende Aufzählungspunkt lautet wie folgt:

  • Für die Binär-Operatoren <<, >> und >>> wird der linke Operand in den Typ T konvertiert, wobei T der erste von int, uint, long und ulong ist, die alle möglichen Werte des Operanden vollständig repräsentieren können. Der Vorgang wird dann mit der Genauigkeit des Typs T durchgeführt, und der Typ des Ergebnisses ist T.

Konstante Ausdrücke

Der Operator >>> wird der Menge der in konstanten Ausdrücken zulässigen Konstrukte unter §12.23 hinzugefügt.

Überladen von Operatoren

Der Operator >>> wird in die Menge der überladbaren binären Operatoren unter §12.4.3 aufgenommen.

„Lifted“ Operatoren

Der Operator >>> wird in die Menge der binären Operatoren aufgenommen, die in §12.4.8 eine Lifted-Form zulassen.

Operatorrangfolge und Assoziativität

Abschnitt §12.4.2 wird geändert, um den >>>-Operator zur Kategorie „Verschiebung“ und den >>>=-Operator zur Kategorie „Zuweisung und Lambda-Ausdruck“ hinzuzufügen.

Grammatische Mehrdeutigkeiten

Der >>>-Operator unterliegt den gleichen grammatikalischen Mehrdeutigkeiten, die in §6.2.5 als regulärer >>-Operator beschrieben sind.

Operatoren

Der Abschnitt §15.10 wird geändert, um den >>>-Operator aufzunehmen.

overloadable_binary_operator
    : '+'   | '-'   | '*'   | '/'   | '%'   | '&'   | '|'   | '^'   | '<<'
    | right_shift | unsigned_right_shift | '=='  | '!='  | '>'   | '<'   | '>='  | '<='
    ;

Binäre Operatoren

Die Signatur eines >>>-Betreibers unterliegt den gleichen Regeln wie die bei §15.10.3 für die Signatur eines >>-Betreibers.

Metadatenname

Abschnitt „I.10.3.2 Binäre Operatoren“ von ECMA-335 hat bereits den Namen für einen Rechtsverschiebungs-Operator ohne Vorzeichen reserviert – op_UnsignedRightShift.

Linq-Ausdrucksbäume

Der >>>-Operator wird in Linq-Ausdrucksbäumen nicht unterstützt, da die Semantik der vordefinierten >>>-Operatoren für vorzeichenbehaftete Typen nicht korrekt dargestellt werden kann, ohne Konvertierungen in einen vorzeichenlosen Typ und zurück hinzuzufügen. Weitere Informationen finden Sie unter https://github.com/dotnet/csharplang/blob/main/meetings/2022/LDM-2022-02-09.md#unsigned-right-shift-operator.

Dynamische Bindung

Es sieht so aus, als ob die dynamische Bindung Werte des Enums System.Linq.Expressions.ExpressionType verwendet, um die Art des binären Operators an den Laufzeit-Binder zu übermitteln. Da es kein Element gibt, das speziell einen Rechtsverschiebungs-Operator ohne Vorzeichen repräsentiert, wird die dynamische Bindung für den >>>-Operator nicht unterstützt, und der Abschnitt über statische und dynamische Bindung (§12.3) wird entsprechend geändert.

Nachteile

Alternativen

Linq-Ausdrucksbäume

Der >>>-Operator wird in Linq-Ausdrucksbäumen unterstützt.

  • Für einen benutzerdefinierten Operator wird ein BinaryExpression-Knoten erstellt, der auf die Operatormethode zeigt.
  • Für vordefinierte Operatoren
    • Wenn der erste Operand ein Typ mit Vorzeichen ist, wird ein BinaryExpression-Knoten erstellt.
    • Wenn der erste Operand ein Typ mit Vorzeichen ist, werden eine Konvertierung für den ersten Operanden in einen Typ ohne Vorzeichen hinzugefügt, ein BinaryExpression-Knoten erstellt und eine Konvertierung für das Ergebnis zurück in den Typ mit Vorzeichen hinzugefügt.

Zum Beispiel:

Expression<System.Func<int, int, int>> z = (x, y) => x >>> y; // (x, y) => Convert((Convert(x, UInt32) >> y), Int32)

Resolution:

Abgelehnt, siehe https://github.com/dotnet/csharplang/blob/main/meetings/2022/LDM-2022-02-09.md#unsigned-right-shift-operator für weitere Informationen.

Ungelöste Fragen

Designbesprechungen

https://github.com/dotnet/csharplang/blob/main/meetings/2022/LDM-2022-02-09.md