Freigeben über


ref readonly Parameter

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 relevanten Anmerkungen 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.

Zusammenfassung

Lassen Sie Regeln für die Änderung der Parameterdeklarationsstelle ref readonly und die Callsite-Änderung wie folgt zu:

Callsite-Anmerkung ref Parameter ref readonly Parameter in Parameter out Parameter
ref Zulässig Zulässig Warnung Fehler
in Fehler Zulässig Zulässig Fehler
out Fehler Fehler Fehler Zulässig
Keine Anmerkung Fehler Warnung Zulässig Fehler

(Beachten Sie, dass es eine Änderung an den bestehenden Regeln gibt: in Parameter mit ref Callsite-Anmerkung erzeugt eine Warnung anstelle eines Fehlers.)

Ändern Sie die Argumentwertregeln wie folgt:

Art des Werts ref Parameter ref readonly Parameter in Parameter out Parameter
rvalue Fehler Warnung Zulässig Fehler
lvalue Zulässig Zulässig Zulässig Zulässig

Wo "lvalue" eine Variable bedeutet (also einen Wert mit einem Speicherort; dieser muss nicht schreib- oder zuweisbar sein) und "rvalue" jede Art von Wert.

Motivation

C# 7.2 hat in-Parameter eingeführt, um schreibgeschützte Verweise zu übergeben. in-Parameter ermöglichen sowohl lvalues als auch rvalues und können ohne Anmerkung an der Callsite verwendet werden. APIs, die Verweise aus ihren Parametern erfassen oder zurückgeben, lassen rvalues jedoch nicht zu und erzwingen auch Hinweise an der Callsite dazu, dass ein Verweis erfasst wird. ref readonly-Parameter sind ideal für solche Fälle geeignet, da sie eine Warnung ausgeben, wenn sie mit rvalues oder ohne jegliche Anmerkung an der Callsite verwendet werden.

Darüber hinaus gibt es APIs, die nur schreibgeschützte Verweise benötigen, jedoch

  • ref-Parameter verwenden, da sie vor der Verfügbarkeit von in eingeführt wurden und ein Wechsel zu in eine disruptive Änderung von Quelle und Binärdatei darstellen würde, z. B. QueryInterface, oder
  • in Parameter, um schreibgeschützte Verweise zu akzeptieren, obwohl das Übergeben von rvalues an diese nicht wirklich sinnvoll ist, z. B. ReadOnlySpan<T>..ctor(in T value)oder
  • ref-Parameter, um rvalues nicht zuzulassen, selbst wenn diese die übergebene Referenz nicht verändern, z. B. Unsafe.IsNullRef.

Diese APIs könnten zu ref readonly-Parametern migrieren, ohne die Benutzer zu beeinträchtigen. Detaillierte Informationen zur binären Kompatibilität finden Sie in der vorgeschlagenen Metadatenkodierung. Insbesondere wäre das Ändern von

  • refref readonly lediglich eine disruptive Binär-Änderung für virtuelle Methoden,
  • refin wäre ebenfalls eine disruptive Binär-Änderung für virtuelle Methoden, jedoch keine disruptive Quell-Änderung (da die Regeln so geändert werden, dass lediglich bei der ref Übergabe von Argumenten an in Parameter gewarnt wird),
  • inref readonly wäre keine disruptive Änderung (aber kein(e) Callsite-Anmerkung oder rvalue würde zu einer Warnung führen),
    • Beachten Sie, dass dies eine disruptive Quelländerung für Benutzer mit älteren Compilerversionen wäre (da diese ref readonly-Parameter als ref-Parameter interpretieren, die in oder keine Anmerkung an der Callsite aufheben nicht zulassen) und neue Compilerversionen mit LangVersion <= 11 (für Konsistenz mit älteren Compilerversionen wird der Fehler ausgegeben, dass ref readonly-Parameter nicht unterstützt werden, es sei denn, die entsprechenden Argumente werden mit dem ref-Modifizierer übergeben).

Umgekehrt wäre die Änderung

  • ref readonlyref eine potenziell disruptive Quelländerung (es sei denn, es wurden nur die ref-Callsite-Anmerkung verwendet und nur schreibgeschützte Referenzen als Argumente verwendet), und eine disruptive Binäränderung für virtuelle Methoden,
  • ref readonlyin wäre keine disruptive Änderung (aber die ref-Callsite-Anmerkung würde zu einer Warnung führen).

Beachten Sie, dass die oben beschriebenen Regeln auf Methodensignaturen, aber nicht auf Stellvertretungssignaturen angewendet werden. Das Ändern von ref zu in in einer Delegatsignatur kann beispielsweise eine disruptive Quelländerung sein (wenn ein Benutzer eine Methode mit einem ref-Parameter diesem Delegattyp zuweist, würde dies nach der API-Änderung zu einem Fehler führen).

detaillierter Entwurf

Im Allgemeinen sind die Regeln für ref readonly-Parameter gleich wie die für in-Parameter in ihrem Vorschlag , außer wenn sie in diesem Vorschlag explizit geändert werden.

Parameterdeklarationen

Es sind keine Grammatikänderungen erforderlich. Der Modifizierer ref readonly ist für Parameter zulässig. Abgesehen von den normalen Methoden dürfen ref readonly für Indexerparameter (wie in, aber im Gegensatz zu ref) verwendet werden, jedoch nicht für Operatorparameter (wie ref, aber im Gegensatz zu in).

Standardwerte sind für ref readonly-Parameter mit einer Warnung zulässig, da sie dem Übergeben von rvalues entsprechen. Auf diese Weise können API-Autoren in-Parameter mit Standardwerten zu ref readonly-Parametern ändern, ohne eine disruptive Quelländerung zu verursachen.

Prüfungen von Wertarten

Beachten Sie, dass, auch wenn der ref-Argumentmodifizierer für ref readonly-Parameter zulässig ist, sich in Bezug auf Wertartüberprüfungen nichts ändert, d. h.,

  • ref kann nur mit zuweisbaren Werten verwendet werden.
  • um schreibgeschützte Verweise zu übergeben, muss stattdessen der Argumentmodifizierer in verwendet werden;
  • um rvalues zu übergeben, darf kein Modifizierer verwendet werden (was zu einer Warnung für ref readonly-Parameter führt, wie in der -Zusammenfassung dieses Vorschlagsbeschrieben).

Überladungsauflösung

Die Überladungsauflösung ermöglicht das Mischen von ref/ref readonly/in/ohne Callsite-Anmerkungen und Parameter-Modifizierer wie in der Tabelle in der Zusammenfassung dieses Vorschlags angegeben,, d. h., alle zuässigen und Warnungs-Fälle gelten bei der Überladungsauflösung als mögliche Kandidaten. Insbesondere gibt es eine Änderung des bestehenden Verhaltens, bei dem Methoden mit dem in-Parameter mit Aufrufen übereinstimmen, wobei das entsprechende Argument mit ref markiert ist – diese Änderung hängt von der LangVersion ab.

Die Warnung beim Übergeben eines Arguments ohne Callsite-Modifizierer an einen ref readonly-Parameter wird jedoch unterdrückt, wenn der Parameter

  • der Empfänger in einem Erweiterungsmethodenaufruf ist,
  • der implizit als Teil eines benutzerdefinierten Auflistungsinitialisierers oder eines Handlers für interpolierte Zeichenfolgen verwendet wird.

Wert-Überladungen werden gegenüber ref readonly-Überladungen bevorzugt, wenn kein Argumentmodifizierer vorhanden ist (in-Parameter zeigen das gleiche Verhalten).

Methodenkonvertierungen

Ebenso werden diese Modifiziere für Konvertierungen von anonymen Funktionen [§10.7] und Methodengruppen [§10.8] als kompatibel betrachtet (aber jede zulässige Konvertierung zwischen verschiedenen Modifizierern führt zu einer Warnung):

  • Der ref readonly-Parameter der Zielmethode darf mit dem in- oder ref-Parameter des Delegaten übereinstimmen.
  • Der in-Parameter der Zielmethode darf mit dem ref readonly- oder, abhängig von der LangVersion, dem ref-Parameter des Delegaten übereinstimmen.
  • Hinweis: Der ref-Parameter der Zielmethode darf nicht mit dem -,-, in- oder ref readonly-Parameter des Delegaten übereinstimmen.

Zum Beispiel:

DIn dIn = (ref int p) => { }; // error: cannot match `ref` to `in`
DRef dRef = (in int p) => { }; // warning: mismatch between `in` and `ref`
DRR dRR = (ref int p) => { }; // error: cannot match `ref` to `ref readonly`
dRR = (in int p) => { }; // warning: mismatch between `in` and `ref readonly`
dIn = (ref readonly int p) => { }; // warning: mismatch between `ref readonly` and `in`
dRef = (ref readonly int p) => { }; // warning: mismatch between `ref readonly` and `ref`
delegate void DIn(in int p);
delegate void DRef(ref int p);
delegate void DRR(ref readonly int p);

Beachten Sie, dass es keine Änderung des Verhaltens von Funktionszeigerkonvertierungen gibt. Zu Erinnerung: Implizite Funktionszeigerkonvertierungen sind nicht zulässig, wenn es einen Konflikt zwischen Verweistypmodifizierern gibt. Explizite Konvertierungen sind immer ohne Warnungen zulässig.

Signaturabgleich

In einem einzelnen Typ deklarierte Mitglieder können sich nicht ausschließlich in ihrer Signatur durch ref/out/in/ref readonlyunterscheiden. Für andere Zwecke des Signaturabgleichs (z. B. Ausblenden oder Überschreiben) ist der Austausch gegen einen ref readonlyin- Modifizierer möglich, dies führt jedoch zu einer Warnung an der Deklarationsstelle [§7.6]. Dies gilt nicht beim Abgleich der partial-Deklaration mit ihrer Implementierung und der Interceptor-Signatur mit der abgefangenen Signatur. Beachten Sie, dass es keine Änderungen beim Überschreiben für ref/in- und ref readonly/ref-Modifier-Paare gibt; sie können nicht ausgetauscht werden, da die Signaturen nicht binär kompatibel sind. Aus Konsistenzgründen gilt dies auch für andere Signaturabgleichszwecke (z. B. Ausblenden).

Metadatencodierung

Als Erinnerung,

  • ref-Parameter werden als einfache byref-Typen ausgegeben (T& in IL),
  • in Parameter sind wie ref, aber sie werden mit System.Runtime.CompilerServices.IsReadOnlyAttributeannotiert. In C# 7.3 und höher werden sie auch mit [in] und, wenn virtuell, mit modreq(System.Runtime.InteropServices.InAttribute)ausgegeben.

ref readonly-Parameter werden als [in] T& ausgegeben und mit dem folgenden Attribut versehen:

namespace System.Runtime.CompilerServices
{
    [AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false, Inherited = false)]
    public sealed class RequiresLocationAttribute : Attribute
    {
    }
}

Darüber hinaus werden sie, falls sie virtuell sind, mit modreq(System.Runtime.InteropServices.InAttribute) emittiert, um die binäre Kompatibilität mit in Parametern sicherzustellen. Beachten Sie, dass im Gegensatz zu in-Parametern keine [IsReadOnly] für ref readonly-Parameter ausgegeben werden, um zu vermeiden, dass der Umfang der Metadaten größer wird, und dass ältere Compilerversionen ref readonly-Parameter als ref-Parameter interpretieren (und daher ist refref readonly keine disruptive Quelländerung, selbst zwischen verschiedenen Compilerversionen).

Das RequiresLocationAttribute wird anhand des namespace-qualifizierten Namens abgeglichen und vom Compiler synthetisiert, wenn es noch nicht in der Kompilierung enthalten ist.

Das Angeben des Attributs in der Quelle ist ein Fehler, wenn es auf einen Parameter angewendet wird, ähnlich wie ParamArrayAttribute.

Funktionszeiger

In Funktionszeigern werden in-Parameter mit modreq(System.Runtime.InteropServices.InAttribute) verwendet (siehe Vorschlag für Funktionszeiger). ref readonly-Parameter werden ohne modreq ausgegeben, sondern stattdessen mit modopt(System.Runtime.CompilerServices.RequiresLocationAttribute). Ältere Compilerversionen ignorieren das modopt und interpretieren deshalb ref readonly-Parameter als ref-Parameter (was dem Verhalten älterer Compilerversionen bei normalen Methoden mit ref readonly-Parametern entspricht, wie oben beschrieben). Neue Compilerversionen, die über das modopt informiert sind, nutzen es hingegen, um ref readonly-Parameter zu erkennen und während -Konvertierungen und -AufrufenWarnungen auszugeben. Für die Konsistenz mit älteren Compilerversionen melden neue Compilerversionen mit LangVersion <= 11 Fehler, dass ref readonly-Parameter nicht unterstützt werden, es sei denn, die entsprechenden Argumente werden mit dem ref-Modifizierer übergeben.

Beachten Sie, dass es sich bei der Änderung von Modifizierern in Funktionszeigersignaturen um eine disruptive Binäränderung handelt, wenn sie Teil öffentlicher APIs sind; daher handelt es sich um eine disruptive Binäränderung, wenn ref oder in zu ref readonlygeändert wird. Eine disruptive Quelländerung tritt jedoch nur für Caller mit LangVersion <= 11 auf, wenn inref readonly geändert wird (wenn der Zeiger mit dem Callsite-Modifizierer in aufgerufen wird), konsistent mit normalen Methoden.

Disruptive Änderungen

Die ref/in Konfliktlockerung bei der Überladungsauflösung führt zu einer disruptiven Verhaltensänderung, wie im nachfolgenden Beispiel illustriert:

class C
{
    string M(in int i) => "C";
    static void Main()
    {
        int i = 5;
        System.Console.Write(new C().M(ref i));
    }
}
static class E
{
    public static string M(this C c, ref int i) => "E";
}

In C# 11 wird der Aufruf an E.Mgebunden, daher wird "E" gedruckt. In C# 12 darf C.M gebunden werden (mit einer Warnung), und Erweiterungsbereiche werden nicht durchsucht, da wir einen verwendbaren Kandidaten haben. Daher wird "C" gedruckt.

Aus demselben Grund liegt auch eine disruptive Quelländerung vor. Im folgenden Beispiel wird "1" in C# 11 gedruckt, aber die Kompilierung in schlägt aufgrund eines Mehrdeutigkeitsfehlers fehl.

var i = 5;
System.Console.Write(C.M(null, ref i));

interface I1 { }
interface I2 { }
static class C
{
    public static string M(I1 o, ref int x) => "1";
    public static string M(I2 o, in int x) => "2";
}

Die obigen Beispiele veranschaulichen die Disruptionen für Methodenaufrufe, aber da sie durch Änderungen bei Überladungsauflösungsänderungen verursacht werden, können sie für Methodenkonvertierungen in ähnlicher Weise ausgelöst werden.

Alternativen

Parameter-Deklarierungen

API-Autoren könnten in-Parameter mit Anmerkungen versehen, die nur lvalues mit einem benutzerdefinierten Attribut akzeptieren, und eine Analyse bereitstellen, um inkorrekte Verwendungen zu kennzeichnen. Dies würde API-Autoren nicht ermöglichen, Signaturen vorhandener APIs zu ändern, die sich für die Verwendung von ref-Parametern entschieden haben, um rvalues nicht zuzulassen. Aufrufer solcher APIs müssen weiterhin zusätzliche Arbeit leisten, um eine ref zu erhalten, wenn sie nur Zugriff auf eine ref readonly-Variable haben. Das Ändern dieser APIs von ref zu [RequiresLocation] in wäre eine disruptive Quelländerung (und bei virtuellen Methoden auch eine disruptive Binäränderung).

Anstatt den Modifizierer ref readonlyzuzulassen, konnte der Compiler erkennen, wann ein spezielles Attribut (wie [RequiresLocation]) auf einen Parameter angewendet wird. Dies wurde in LDM 2022-04-25erörtert, wobei dies ein Sprachmerkmal ist, kein Analysewerkzeug, daher sollte es auch wie eines aussehen.

Prüfungen von Wertarten

Das Übergeben von lvalues ohne Modifizierer an ref readonly-Parameter könnte ohne Warnungen zugelassen werden, ähnlich den impliziten byref-Parametern in C++. Dies wurde in LDM 2022-05-11erörtert, wobei festgestellt wurde, dass der Hauptgrund für ref readonly-Parameter APIs sind, die Verweise aus diesen Parametern erfassen oder zurückgeben. Daher ist eine Art Markierung von Vorteil.

Die Übergabe eines rvalue an eine ref readonly könnte ein Fehler und keine Warnung sein. Das wurde zunächst in LDM 2022-04-25akzeptiert, aber in späteren E-Mail-Diskussionen wurde dies jedoch relativiert, da dabei die Möglichkeit verloren ginge, vorhandene APIs zu ändern, ohne die Benutzer zu beeinträchtigen.

in könnte der „natürliche“ Callsite-Modifizierer für ref readonly-Parameter sein, und die Verwendung von ref könnte zu Warnungen führen. Dadurch wird ein konsistenter Code-Stil sichergestellt, und an der Callsite wäre offensichtlich, dass der Verweis schreibgeschützt ist (im Gegensatz zu ref). Dies wurde in LDM 2022-04-25zuerst akzeptiert. Warnungen können jedoch ein Reibungspunkt für API-Autoren sein, um von ref zu ref readonlyzu wechseln. Außerdem wurde in als ref readonly plus Komfortmerkmale neu definiert, weshalb dies in LDM 2022-05-11abgelehnt wurde.

LDM-Überprüfung ausstehend

In C# 12 wurden keine der folgenden Optionen implementiert. Diese bleiben potenzielle Vorschläge.

Parameter-Deklarierungen

Umgekehrte Sortierung von Modifizierern (readonly ref anstelle von ref readonly) kann zulässig sein. Dies wäre inkonsistent mit der Art und Weise, in der sich readonly ref Rückgaben und Felder verhalten (umgekehrte Sortierung ist nicht zulässig oder hat eine andere Bedeutung) und könnte in Zukunft mit schreibgeschützten Parametern in Konflikt geraten.

Standardwerte könnten für die ref readonly-Parameter fehlerhaft sein.

Prüfungen von Wertarten

Fehler können anstelle von Warnungen ausgegeben werden, wenn rvalues an ref readonly-Parameter übergeben oder Callsite-Anmerkungen und Parametermodifizierer nicht übereinstimmen. Ebenso kann spezielles modreq anstelle eines Attributs verwendet werden, damit ref readonly-Parameter sich von in-Parametern auf Binär-Ebene unterscheiden. Dies würde stärkere Garantien bieten und wäre daher für neue APIs sinnvoll, würde jedoch die Einführung in bestehende Runtime-APIs verhindern, bei denen keine disruptiven Änderungen zulässig sind.

Wertartenüberprüfungen könnten gelockert werden, um schreibgeschützte Referenzen über ref in in/ref readonly-Parameter zu übergeben. Das wäre vergleichbar damit, wie Zuweisungen und Rückgaben von Verweisen derzeit funktionieren – sie ermöglichen auch das Übergeben von Verweisen in schreibgeschützter Form über den ref-Modifizierer im Quellausdruck. Die ref befindet sich jedoch in der Regel nahe der Stelle, an der das Ziel als ref readonly deklariert wird. Daher ist klar, dass wir einen Verweis in schreibgeschützter Form übergeben, im Gegensatz zu Aufrufen, deren Argument- und Parametermodifizierer sich normalerweise weit voneinander entfernt befinden. Darüber hinaus erlauben sie nur den ref-Modifizierer, im Gegensatz zu Argumenten, die auch in zulassen; daher würden in und ref gegen Argumente austauschbar werden, oder in würden praktisch veraltet werden, wenn Benutzer ihren Code konsistent machen wollten (sie würden wahrscheinlich überall ref verwenden, da dies der einzige Modifizierer ist, der für Zuweisungen und Rückgaben von Referenzen zulässig ist).

Überladungsauflösung

Überladungsauflösung, Überschreibung und Konvertierung könnten die Austauschbarkeit von ref readonly und in-Modifizierern unzulässig machen.

Die Änderung der Überladungsauflösung für vorhandene in-Parameter könnte bedingungslos übernommen werden (ohne Berücksichtigung der LangVersion). Dies wäre jedoch eine disruptive Änderung.

Der Aufruf einer Erweiterungsmethode mit dem Empfänger ref readonly kann die Warnung „Argument 1 sollte mit dem Schlüsselwort ref oder in übergeben werden“ auslösen, ähnlich wie dies bei Nicht-Erweiterungsaufrufen ohne Callsite-Modifizierer vorkommen würde (der Benutzer könnte diese Warnung beheben, indem der Aufruf der Erweiterungsmethode in einen statischen Methodenaufruf umgewandelt wird). Dieselbe Warnung kann erfolgen, wenn ein benutzerdefinierter Sammlungsinitialisierer oder ein Handler für interpolierte Zeichenfolgen mit dem Parameter ref readonly verwendet wird, auch wenn der Benutzer keinen Workaround dafür verwenden konnte.

ref readonly-Überladungen könnten gegenüber Wert-Überladungen bevorzugt werden, wenn kein Callsite-Modifizierer vorhanden ist, oder wenn ein Mehrdeutigkeitsfehler vorliegen könnte.

Methodenkonvertierungen

Wir könnten zulassen, dass der ref-Parameter der Zielmethode dem in- und ref readonly-Parameter des Delegaten entspricht. Dies würde es API-Autoren ermöglichen, beispielsweise in Delegatensignaturen ref zu in zu ändern, ohne ihre Benutzer zu beeinträchtigen, was konsistent mit dem ist, was für normale Methodensignaturen zulässig ist. Dies würde jedoch auch zu folgendem Verstoß gegen readonly- Garantien, lediglich mit einer Warnung, führen:

class Program
{
    static readonly int f = 123;
    static void Main()
    {
        var d = (in int x) => { };
        d = (ref int x) => { x = 42; }; // warning: mismatch between `ref` and `in`
        d(f); // changes value of `f` even though it is `readonly`!
        System.Console.WriteLine(f); // prints 42
    }
}

Konvertierungen von Funktionszeigern könnten bei ref readonly/ref/in-Konflikten warnen, aber um dies von der LangVersion abhängig zu machen, wäre eine erhebliche Implementierungsinvestition erforderlich, da Typkonvertierungen derzeit keinen Zugriff auf den Kompilierungsprozess erfordern. Auch wenn ein Konflikt derzeit als Fehler gilt, ist es für Benutzer einfach, eine Umwandlung hinzuzufügen, um den Konflikt bei Bedarf zuzulassen.

Codierung von Metadaten

Das Angeben von RequiresLocationAttribute in der Quelle könnte zulässig sein, ähnlich wie die Attribute In und Out. Alternativ kann es sich um einen Fehler handeln, wenn er in anderen Kontexten als nur Parameter angewendet wird, ähnlich wie das IsReadOnly-Attribut, um zusätzlichen Gestaltungsspielraum zu bewahren.

Funktionszeiger-ref readonly-Parameter können mit unterschiedlichen modopt/modreq Kombinationen ausgegeben werden (beachten Sie, dass „disruptive Quelländerung“ in dieser Tabelle für Aufrufer mit LangVersion <= 11 bedeutet):

Zusatztasten Kann über Kompilierungen hinweg erkannt werden Alte Compiler sehen sie als refref readonly inref readonly
modreq(In) modopt(RequiresLocation) Ja in disruptive Binär-, Quelländerung disruptive Binäränderung
modreq(In) Nein in disruptive Binär-, Quelländerung Okay
modreq(RequiresLocation) Ja nicht unterstützt disruptive Binär-, Quelländerung disruptive Binär-, Quelländerung
modopt(RequiresLocation) Ja ref disruptive Binäränderung disruptive Binär-, Quelländerung

Wir könnten sowohl die Attribute [RequiresLocation] als auch [IsReadOnly] für die ref readonly-Parameter ausgeben. Dann wäre inref readonly selbst für ältere Compilerversionen keine disruptive Quelländerung aber refref readonly wäre eine disruptive Quelländerung für ältere Compiler (da diese ref readonly als in interpretieren würden, wodurch ref-Modifizierer nicht zulässig wären) und neuere Compilerversionen mit LangVersion <= 11 (aus Konsistenzgründen).

Wir könnten das Verhalten für LangVersion <= 11 von dem Verhalten für ältere Compilerversionen unterscheiden. Beispielsweise kann es sich um einen Fehler handeln, wenn ein ref readonly-Parameter aufgerufen wird (auch wenn der ref-Modifizierer an der Callsite verwendet wird), oder dies kann immer ohne Fehler zulässig sein.

Disruptive Änderungen

Dieser Vorschlag schlägt vor, eine disruptive Verhaltensänderung zu akzeptieren, da dies selten auftreten sollte, durch die LangVersion eingeschränkt wird, und Benutzer sie umgehen können, indem sie die Erweiterungsmethode explizit aufrufen. Stattdessen könnten wir dies beheben, indem wir

  • den ref/in-Konflikt nicht zulassen (dies würde lediglich die Migration zu in für alte APIs verhindern, die ref verwendet haben, da in noch nicht verfügbar war),
  • Ändern der Regeln für die Überladungsauflösung, um weiterhin nach einer besseren Übereinstimmung zu suchen (bestimmt durch die unten angegebenen Verbesserungsregeln), wenn in diesem Vorschlag ein solcher Konflikt eingeführt wurde,
    • oder alternativ nur bei ref vs. in-Konflikt fortfahren, nicht für andere (ref readonly vs. ref/in/nach-Wert).
Verbesserungsregeln

Das folgende Beispiel führt derzeit zu drei Mehrdeutigkeitsfehlern für die drei Aufrufe von M. Wir könnten neue Verbesserungsregeln hinzufügen, um die Mehrdeutigkeiten aufzulösen. Dies würde auch die zuvor beschriebene disruptive Quelländerung beheben. Eine Möglichkeit wäre, das Beispiel 221 drucken zu lassen, wobei der ref readonly-Parameter mit dem in-Argument abgeglichen wird, da es zu einer Warnung führen würde, es ohne Modifizierer aufzurufen, während dies für den in-Parameter zulässig ist.

interface I1 { }
interface I2 { }
class C
{
    static string M(I1 o, in int i) => "1";
    static string M(I2 o, ref readonly int i) => "2";
    static void Main()
    {
        int i = 5;
        System.Console.Write(M(null, ref i));
        System.Console.Write(M(null, in i));
        System.Console.Write(M(null, i));
    }
}

Neue Verbesserungsregeln könnten den Parameter, dessen Argument mit einem anderen Argumentmodifizierer übergeben worden sein könnte, als schlechter markieren, um ihn besser zu machen. Mit anderen Worten: Der Benutzer sollte immer in der Lage sein, einen schlechteren Parameter in einen besseren Parameter umzuwandeln, indem er seinen entsprechenden Argumentmodifizierer ändert. Wenn beispielsweise ein Argument von inübergeben wird, wird ein ref readonly-Parameter gegenüber einem in-Parameter bevorzugt, da der Benutzer das Argument nach Wert übergeben kann, um den in-Parameter auszuwählen. Diese Regel ist nur eine Erweiterung der Nach-Wert-/inPräferenzregel, die derzeit gilt (es handelt sich um die letzte Überladungsauflösungsregel und die gesamte Überladung ist besser, wenn ein Parameter besser und keiner schlechter als der entsprechende Parameter einer anderen Überladung ist).

Argument besserer Parameter schlechterer Parameter
ref/in ref readonly in
ref ref ref readonly/in
Nach Wert Nach Wert/in ref readonly
in in ref

Wir sollten Methodenkonvertierungen ähnlich behandeln. Das folgende Beispiel führt derzeit zu zwei Mehrdeutigkeitsfehlern für die beiden Delegatzuweisungen. Neue Verbesserungsregeln könnten einen Methodenparameter bevorzugen, dessen Refness-Modifizierer dem entsprechenden Refness-Modifizierer des Zieldelegatparameters entspricht, gegenüber einem solchen, bei dem sie nicht übereinstimmen. Daher würde das folgende Beispiel 12drucken.

class C
{
    void M(I1 o, ref readonly int x) => System.Console.Write("1");
    void M(I2 o, ref int x) => System.Console.Write("2");
    void Run()
    {
        D1 m1 = this.M;
        D2 m2 = this.M; // currently ambiguous

        var i = 5;
        m1(null, in i);
        m2(null, ref i);
    }
    static void Main() => new C().Run();
}
interface I1 { }
interface I2 { }
class X : I1, I2 { }
delegate void D1(X s, ref readonly int x);
delegate void D2(X s, ref int x);

Design Meetings