Udostępnij za pośrednictwem


parametry ref readonly

Notatka

Ten artykuł jest specyfikacją funkcji. Specyfikacja służy jako dokument projektowy dla funkcji. Zawiera proponowane zmiany specyfikacji wraz z informacjami wymaganymi podczas projektowania i opracowywania funkcji. Te artykuły są publikowane do momentu sfinalizowania proponowanych zmian specyfikacji i włączenia ich do obecnej specyfikacji ECMA.

Mogą wystąpić pewne rozbieżności między specyfikacją funkcji a ukończoną implementacją. Te różnice są przechwytywane w odpowiednich spotkania projektowego języka (LDM).

Więcej informacji na temat procesu wdrażania specyfikacji funkcji można znaleźć w standardzie języka C# w artykule dotyczącym specyfikacji .

Problem z mistrzem: https://github.com/dotnet/csharplang/issues/6010

Streszczenie

Zezwalaj na modyfikator deklaracji parametru lokacji ref readonly i zmień reguły wywołania lokacji w następujący sposób:

Adnotacja miejsca wywołania parametr ref parametr ref readonly parametr in parametr out
ref Dozwolone Dozwolone Ostrzeżenie Błąd
in Błąd Dozwolone Dozwolone Błąd
out Błąd błąd Błąd Dozwolone
Brak adnotacji Błąd Ostrzeżenie Dozwolone Błąd

(Należy pamiętać, że istnieje jedna zmiana w istniejących regułach: parametr in z adnotacją wywołania ref produkuje ostrzeżenie zamiast błędu.)

Zmień reguły wartości argumentu w następujący sposób:

Rodzaj wartości parametr ref parametr ref readonly parametr in parametr out
rvalue Błąd ostrzeżenie Dozwolone Błąd
Lvalue Dozwolone Dozwolone Dozwolone Dozwolone

Jeśli wartość lvalue oznacza zmienną (tj. wartość z lokalizacją; nie musi być zapisywalna/przypisywana) i wartość rvalue oznacza dowolny rodzaj wartości.

Motywacja

Język C# 7.2 wprowadził in parametry jako sposób przekazywania odwołań tylko do odczytu. in parametry zezwalają zarówno na wartości lvalue, jak i rvalue i można je stosować bez adnotacji w miejscach wywołań. Jednak interfejsy API, które przechwytują lub zwracają odwołania z ich parametrów, chcą uniemożliwić użycie wartości rvalue, a także wymusić pewne wskazanie w miejscu wywołania, że odwołanie jest przechwytywane. ref readonly parametry są idealne w takich przypadkach, ponieważ ostrzegają, jeśli są używane z rvalues lub bez adnotacji w miejscu wywołania.

Ponadto istnieją interfejsy API, które wymagają wyłącznie odwołań do odczytu, ale nadal je używają.

  • ref parametrów, ponieważ zostały wprowadzone przed udostępnieniem in, a zmiana na in będzie zmianą łamiącą zgodność na poziomie źródła i binariów, np. QueryInterfacelub
  • in parametrów umożliwiających przyjmowanie odwołań tylko do odczytu, mimo że przekazywanie do nich wartości rvalue nie ma większego uzasadnienia, np. ReadOnlySpan<T>..ctor(in T value)lub
  • ref parametry, aby zabronić użycia rvalue, nawet jeśli nie zmieniają przekazanego odwołania, np. Unsafe.IsNullRef.

Te interfejsy API mogą migrować do parametrów ref readonly bez przerywania pracy użytkowników. Aby uzyskać szczegółowe informacje na temat zgodności binarnej, zobacz proponowane kodowanie metadanych . W szczególności zmiana

  • refref readonly byłaby tylko zmianą binarną powodującą niekompatybilność dla metod wirtualnych.
  • refin byłaby również zmianą wprowadzającą niezgodność binarną dla metod wirtualnych, ale nie zmianą wprowadzającą niezgodność źródła (ponieważ reguły zmieniają się, aby ostrzegać tylko przed argumentami ref przekazanymi do parametrów in),
  • inref readonly nie byłaby zmianą powodującą niezgodność (ale brak adnotacji wywołania lub wartości rvalue nie spowoduje ostrzeżenia),
    • Należy pamiętać, że byłaby to zmiana powodująca niezgodność źródła dla użytkowników korzystających ze starszych wersji kompilatora (ponieważ interpretują parametry ref readonly jako parametry ref, co uniemożliwia in lub brak adnotacji w wywołaniach) oraz nowe wersje kompilatora z LangVersion <= 11 (dla zapewnienia spójności ze starszymi wersjami kompilatora, zostanie zgłoszony błąd informujący, że parametry ref readonly nie są obsługiwane, chyba że odpowiednie argumenty są przekazywane z modyfikatorem ref).

W przeciwnym kierunku następuje zmiana

  • ref readonlyref byłaby potencjalnie zmianą powodującą niezgodność źródła (chyba że użyto tylko adnotacji miejsca wywołania ref i użyto jedynie odwołań tylko do odczytu jako argumentów) oraz zmianą powodującą niezgodność binarną dla metod wirtualnych.
  • ref readonlyin nie byłby zmianą powodującą niezgodność (ale adnotacja miejsc wywołania ref spowodowałaby ostrzeżenie).

Należy pamiętać, że opisane powyżej reguły mają zastosowanie do podpisów metod, ale nie do delegowania podpisów. Na przykład zmiana ref na in w podpisie delegata może być zmianą powodującą niezgodność w kodzie źródłowym (jeśli użytkownik przypisuje metodę z parametrem ref do delegata tego typu, spowoduje to błąd po zmianie interfejsu API).

Szczegółowy projekt

Ogólnie rzecz biorąc, reguły dotyczące parametrów ref readonly są takie same jak określone dla parametrów in w ich propozycji, z wyjątkiem przypadków jawnie zmienionych w niniejszej propozycji.

Deklaracje parametrów

Nie są konieczne żadne zmiany w gramatyce. Modyfikator ref readonly będzie dozwolony dla parametrów. Oprócz normalnych metod ref readonly będą dozwolone dla parametrów indeksatora (takich jak in, ale w przeciwieństwie do ref), ale niedozwolone dla parametrów operatora (takich jak ref, ale w przeciwieństwie do in).

Domyślne wartości parametrów będą dozwolone dla parametrów ref readonly z ostrzeżeniem, ponieważ są one równoważne z przekazywaniem wartości rvalue. Dzięki temu autorzy interfejsu API mogą zmieniać parametry in z wartościami domyślnymi na parametry ref readonly, nie wprowadzając zmian, które łamałyby zgodność ze źródłem.

Sprawdzanie rodzaju wartości

Należy pamiętać, że mimo iż modyfikator argumentu ref jest dozwolony dla parametrów ref readonly, nic się nie zmienia w odniesieniu do sprawdzania rodzaju wartości, tj.

  • ref można używać tylko z wartościami, które można przypisać;
  • aby przekazać odwołania tylko do odczytu, należy zamiast tego użyć modyfikatora argumentu in;
  • aby przekazać wartości rvalue, należy używać bez modyfikatora (co prowadzi do ostrzeżenia dla parametrów ref readonly, zgodnie z opisem w podsumowaniu tej propozycji ).

Rozpoznawanie przeciążenia

Rozpoznawanie przeciążeń umożliwi mieszanie adnotacji ref/ref readonly/in/no callsite i modyfikatorów parametrów zgodnie z opisem w tabeli w podsumowanie tej propozycji, tj. wszystkie dozwolone i przypadków ostrzegawczych będą traktowane jako możliwe kandydatów podczas rozwiązywania przeciążenia. W szczególności zmieni się istniejące zachowanie, gdzie metody z parametrem in będą pasować do wywołań z odpowiadającym argumentem oznaczonym jako ref— ta zmiana będzie kontrolowana przez wersję języka (LangVersion).

Jednak ostrzeżenie dotyczące przekazywania argumentu bez modyfikatora miejsca wywołania do parametru ref readonly zostanie pominięte, jeśli parametr jest

  • odbiorca w wywołaniu metody rozszerzenia,
  • używany niejawnie jako część niestandardowego inicjatora kolekcji lub procedury obsługi ciągów interpolowanych.

Przeciążenia przez wartość będą preferowane nad przeciążeniami ref readonly, gdy nie ma modyfikatora argumentu (parametryin mają takie samo zachowanie).

Konwersje metod

Podobnie, w kontekście funkcji anonimowej [§10.7] oraz grupy metod [§10.8], te modyfikatory są uznawane za zgodne (ale każda dozwolona konwersja między różnymi modyfikatorami powoduje ostrzeżenie):

  • ref readonly parametr metody docelowej może być zgodny z parametrem in lub ref delegata,
  • in parametr metody docelowej może odpowiadać ref readonly lub, uzależnione od wersji języka, ref parametrowi delegata.
  • Uwaga: ref parametr metody docelowej nie jest dozwolony do dopasowania in ani parametru ref readonly delegata.

Na przykład:

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);

Należy pamiętać, że nie ma żadnych zmian w zachowaniu konwersji wskaźników funkcji . Przypominamy, że niejawne konwersje wskaźników funkcji są niedozwolone, jeśli istnieje niezgodność między modyfikatorami rodzaju odniesienia, a jawne rzutowania są zawsze dozwolone bez żadnych ostrzeżeń.

Dopasowywanie podpisów

Elementy członkowskie zadeklarowane w jednym typie nie mogą różnić się podpisem wyłącznie przez ref/out/in/ref readonly. W innych celach dopasowywania podpisów (np. ukrywania lub zastępowania) można wymienić ref readonly z modyfikatorem in, ale powoduje to ostrzeżenie w miejscu deklaracji [§7.6]. Nie ma to zastosowania podczas dopasowywania deklaracji partial z jego implementacją i dopasowywania podpisu przechwytującego z przechwyconym podpisem. Należy pamiętać, że nie ma żadnych zmian w zastępowaniu par modyfikatorów ref/in i ref readonly/ref; nie można ich zamieniać miejscami, ponieważ podpisy nie są zgodne binarnie. W przypadku spójności to samo dotyczy innych celów dopasowywania podpisów (np. ukrywania).

Kodowanie metadanych

Przypomnienie:

  • ref parametry są emitowane jako proste typy byref (T& w IL),
  • in parametry są podobne do ref oraz są oznaczone adnotacjami z System.Runtime.CompilerServices.IsReadOnlyAttribute. W języku C# 7.3 lub nowszym są one również emitowane z [in] i jeśli są wirtualne, modreq(System.Runtime.InteropServices.InAttribute).

ref readonly parametry będą emitowane jako [in] T&, a także oznaczone następującym atrybutem:

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

Ponadto, jeśli są wirtualne, będą emitowane z modreq(System.Runtime.InteropServices.InAttribute), aby zapewnić zgodność binarną z parametrami in. Należy pamiętać, że w odróżnieniu od parametrów in, żadne [IsReadOnly] nie będą emitowane dla parametrów ref readonly, aby uniknąć zwiększenia rozmiaru metadanych, a także umożliwienia starszym wersjom kompilatora interpretacji parametrów ref readonly jako parametrów ref (i tym samym refref readonly nie będzie niezgodnością źródła nawet między różnymi wersjami kompilatora).

RequiresLocationAttribute zostaną dopasowane przez kwalifikowaną nazwę przestrzeni nazw i zsyntetyzowane przez kompilator, jeśli nie zostały jeszcze uwzględnione w kompilacji.

Określenie atrybutu w źródle będzie błędem, jeśli zostanie zastosowany do parametru, podobnie jak ParamArrayAttribute.

Wskaźniki funkcji

W wskaźnikach funkcji parametry in są emitowane przy użyciu modreq(System.Runtime.InteropServices.InAttribute) (zobacz propozycję wskaźników funkcji). ref readonly parametry będą emitowane bez tego modreq, ale zamiast tego modopt(System.Runtime.CompilerServices.RequiresLocationAttribute). Starsze wersje kompilatora zignorują modopt, dlatego zinterpretują parametry ref readonly jako parametry ref (zgodne ze starszym zachowaniem kompilatora dla normalnych metod z parametrami ref readonly opisanymi powyżej), a nowe wersje kompilatora świadome modopt użyją go, aby rozpoznać parametry ref readonly i emitować ostrzeżenia podczas konwersji oraz wywołań . W celu zapewnienia spójności ze starszymi wersjami kompilatora, nowe wersje kompilatora z LangVersion <= 11 będą zgłaszać błędy informujące, że parametry ref readonly nie są obsługiwane, chyba że odpowiednie argumenty zostaną przekazane z modyfikatorem ref.

Należy pamiętać, że jest to przerwa binarna umożliwiająca zmianę modyfikatorów w sygnaturach wskaźnika funkcji, jeśli są one częścią publicznych interfejsów API, dlatego będzie to przerwa binarna podczas zmiany ref lub in na ref readonly. Jednak przerwa źródłowa będzie występować tylko w przypadku wywołań z LangVersion <= 11 podczas zmiany inref readonly (jeśli wywoływanie wskaźnika z modyfikatorem w miejscu wywołania in), zgodnie z normalnymi metodami.

Zmiany powodujące niezgodność

Rozwiązanie niezgodności ref/in w rozwiązywaniu przeciążenia funkcji wprowadza zmianę powodującą zmianę zachowania, co zostało pokazane w poniższym przykładzie.

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";
}

W języku C# 11 wywołanie wiąże się z elementem E.M, dlatego "E" jest wyświetlane. W C# 12 C.M może być użyty do wiązania (z ostrzeżeniem) i nie są przeszukiwane zakresy rozszerzeń, ponieważ mamy odpowiedniego kandydata, dlatego "C" jest wypisany.

Również istnieje zmiana źródłowa powodująca niekompatybilność z tego samego powodu. Poniższy przykład wyświetla "1" w C# 11, ale nie udaje się go skompilować z powodu błędu niejednoznaczności w C# 12.

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";
}

W powyższych przykładach pokazano przerwy przy wywołaniach metod, ale ponieważ są one spowodowane zmianami w rozwiązaniu przeciążenia, mogą być w podobny sposób aktywowane w przypadku konwersji metod.

Alternatywy

deklaracje parametrów

Autorzy interfejsu API mogą dodawać adnotacje in parametrów zaprojektowanych tak, aby akceptowały tylko lvalue z atrybutem niestandardowym i udostępniać analizator do flagowania nieprawidłowego użycia. Nie pozwoliłoby to autorom interfejsów API na zmianę podpisów istniejących interfejsów API, które zdecydowały się używać parametrów ref, aby uniemożliwić rvalues. Osoby wywołujące takie interfejsy API nadal muszą wykonać dodatkową pracę, aby uzyskać ref, jeśli mają dostęp tylko do zmiennej ref readonly. Zmiana tych interfejsów API z ref na [RequiresLocation] in spowodowałaby niekompatybilność źródła (a w przypadku metod wirtualnych także niekompatybilność binarną).

Zamiast zezwalać modyfikatorowi ref readonly, kompilator może rozpoznać, kiedy do parametru jest stosowany atrybut specjalny (na przykład [RequiresLocation]). Zostało to omówione w LDM 2022-04-25, decydując, że jest to funkcja języka, a nie analizator, więc powinna wyglądać jak jedna.

sprawdzanie typu wartości

Przekazywanie wartości lvalue bez żadnych modyfikatorów do ref readonly parametrów może być dozwolone bez żadnych ostrzeżeń, podobnie jak niejawne parametry byref języka C++. Zostało to omówione w LDM 2022-05-11, zauważając, że główną motywacją dla parametrów ref readonly są interfejsy API, które z tych parametrów przechwytują lub zwracają referencje, dlatego znaczniki pewnego rodzaju są korzystnym rozwiązaniem.

Przekazywanie wartości rvalue do ref readonly może być błędem, a nie ostrzeżeniem. Zostało to początkowo zaakceptowane w LDM 2022-04-25, ale późniejsze dyskusje e-mailowe złagodziły to, ponieważ stracilibyśmy możliwość zmiany istniejących interfejsów API bez zakłócania doświadczeń użytkowników.

in może być "naturalnym" modyfikatorem dla miejsc wywołań z parametrami ref readonly, a użycie ref może spowodować wyświetlenie ostrzeżeń. Zapewni to spójny styl kodu i uczyni oczywistym w miejscu wywołania, że odwołanie jest tylko do odczytu (w przeciwieństwie do ref). Został on początkowo zaakceptowany w LDM 2022-04-25. Jednak ostrzeżenia mogą być punktem tarć dla autorów interfejsów API, aby przejść z ref do ref readonly. Ponadto in została ponownie zdefiniowana jako ref readonly plus funkcje ułatwiające użytkowanie, dlatego redefinicja została odrzucona w LDM 2022-05-11.

Oczekująca recenzja LDM

Żadna z poniższych opcji nie została zaimplementowana w języku C# 12. Pozostają one potencjalnymi propozycjami.

deklaracje parametrów

Odwrotne porządkowanie modyfikatorów (readonly ref zamiast ref readonly) może być dozwolone. Byłoby to niezgodne ze sposobem działania zwrotów i pól readonly ref (odwrócona kolejność jest niedozwolona lub oznacza coś innego) i może kolidować z parametrami tylko do odczytu, jeśli zostaną zaimplementowane w przyszłości.

Wartości parametrów domyślnych mogą być błędem dla parametrów ref readonly.

Sprawdzanie rodzaju wartości

Błędy mogą być wygenerowane zamiast ostrzeżeń podczas przekazywania tzw. „rvalue” do parametrów ref readonly lub niewłaściwego dopasowania adnotacji wywołań i modyfikatorów parametrów. Podobnie można użyć specjalnych modreq zamiast atrybutu, aby upewnić się, że parametry ref readonly różnią się od parametrów in na poziomie binarnym. Zapewniłoby to silniejsze gwarancje, więc byłoby dobre dla nowych interfejsów API, ale uniemożliwiłoby wdrożenie istniejących interfejsów API środowiska uruchomieniowego, które nie mogą wprowadzać zmian powodujących niezgodność.

Sprawdzanie rodzaju wartości może być złagodzone, aby umożliwić przekazywanie odwołań tylko do odczytu za pośrednictwem ref do parametrów in/ref readonly. Byłoby to podobne do tego, w jaki sposób przypisania ref i zwroty ref działają dzisiaj — umożliwiają również przekazywanie odwołań jako readonly za pośrednictwem modyfikatora ref w wyrażeniu źródłowym. Jednak ref zwykle znajduje się blisko miejsca, w którym obiekt docelowy jest deklarowany jako ref readonly, więc jasne jest, że przekazujemy odwołanie jako readonly, w przeciwieństwie do wywołań, których argument i modyfikatory parametrów są zwykle daleko od siebie. Ponadto umożliwiają one tylko modyfikator ref w przeciwieństwie do argumentów, które zezwalają również na in, dlatego in i ref stałyby się wymienne dla argumentów lub in stałyby się praktycznie przestarzałe, gdyby użytkownicy chcieli zapewnić spójność kodu (prawdopodobnie będą używać ref wszędzie, ponieważ jest to jedyny modyfikator dozwolony dla przypisań ref i zwrotów ref).

Rozwiązywanie przeciążeń

Rozpoznawanie przeciążeń, zastępowanie i konwersja mogą uniemożliwić zamienność modyfikatorów ref readonly i in.

Zmiana rozdzielczości przeciążenia dla istniejących parametrów in może zostać podjęta bezwarunkowo (nie biorąc pod uwagę LangVersion), ale byłaby to zmiana powodująca niezgodność.

Wywołanie metody rozszerzenia za pomocą odbiornika ref readonly może spowodować wyświetlenie ostrzeżenia "Argument 1 powinien zostać przekazany z słowem kluczowym ref lub in", podobnie jak ma to miejsce w przypadku wywołań innych niż rozszerzenia, bez modyfikatorów lokalizacji wywołań. Użytkownik może naprawić takie ostrzeżenie, zamieniając wywołanie metody rozszerzenia na wywołanie metody statycznej. To samo ostrzeżenie można zgłosić podczas używania niestandardowego inicjatora kolekcji lub procedury obsługi ciągów interpolowanych z parametrem ref readonly, chociaż użytkownik nie mógł obejść tego parametru.

ref readonly przeciążenia mogą być preferowane zamiast przeciążeń według wartości, gdy nie ma modyfikatora wywołań lub może wystąpić błąd niejednoznaczności.

Konwersje metod

Możemy zezwolić, by parametr ref metody docelowej dopasował się do parametrów in i ref readonly delegata. Umożliwiłoby to autorom interfejsów API zmianę na przykład ref na in w podpisach delegatów bez powodowania problemów dla użytkowników, zgodnie z tym, co jest dozwolone dla normalnych podpisów metod. Spowodowałoby to jednak również następujące naruszenie gwarancji readonly zaledwie ostrzeżeniem:

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
    }
}

Konwersje wskaźników funkcji mogą ostrzegać przed niezgodnością ref readonly/ref/in, ale jeśli chcielibyśmy odwołać się do LangVersion, wymagałoby to znaczącej inwestycji w implementację, ponieważ dzisiejsze konwersje typów nie potrzebują dostępu do kompilowania. Ponadto, mimo że niezgodność jest obecnie błędem, użytkownicy mogą łatwo dodać rzutowanie, aby umożliwić niezgodność, jeśli chcą.

kodowanie metadanych

Określanie RequiresLocationAttribute w źródle może być dozwolone, podobnie jak atrybuty In i Out. Alternatywnie może stanowić błąd, gdy jest stosowane w innych kontekstach niż jedynie w odniesieniu do parametrów, podobnie jak w przypadku atrybutu IsReadOnly; co pozwala zachować dodatkową przestrzeń projektową.

Wskaźnik funkcji dla parametrów ref readonly może być emitowany z różnymi kombinacjami modopt/modreq (zwróć uwagę, że "przerwa źródłowa" w tej tabeli oznacza dla wywołujących z LangVersion <= 11):

Modyfikatory Można rozpoznać w różnych kompilacjach Stare kompilatory postrzegają je jako refref readonly inref readonly
modreq(In) modopt(RequiresLocation) tak in binarny, przerwa w źródle przerwa binarna
modreq(In) Nie in binarny, przerwa źródłowa Ok
modreq(RequiresLocation) tak Nieobsługiwane binarny, przerwa źródłowa binarny, przerwa źródła
modopt(RequiresLocation) tak ref przerwa binarna binarny, przerwa źródłowa

Możemy emitować zarówno atrybuty [RequiresLocation], jak i [IsReadOnly] dla parametrów ref readonly. Następnie inref readonly nie byłaby zmianą naruszającą kompatybilność nawet w przypadku starszych wersji kompilatora, ale refref readonly stałaby się zmianą naruszającą kompatybilność źródła dla starszych wersji kompilatora (ponieważ starsze kompilatory zinterpretują ref readonly jako in, uniemożliwiając użycie modyfikatorów ref) a nowsze wersje kompilatora uwzględniające LangVersion <= 11 (w celu zapewnienia spójności).

Moglibyśmy sprawić, że zachowanie LangVersion <= 11 będzie inne niż zachowanie starszych wersji kompilatora. Na przykład może to być błąd za każdym razem, gdy jest wywoływany parametr ref readonly (nawet w przypadku używania modyfikatora ref w miejscu wywołania), lub zawsze może być dozwolony i nie powoduje żadnych błędów.

zmiany łamiące zgodność

Ta propozycja sugeruje zaakceptowanie zmiany powodującej niekompatybilność, ponieważ powinna zdarzać się rzadko, jest kontrolowana przez LangVersion, a użytkownicy mogą ją obejść, jawnie wywołując metodę rozszerzenia. Zamiast tego możemy temu zapobiec

  • uniemożliwienie niezgodności ref/in (uniemożliwiłoby to tylko migrację do in dla starych interfejsów API, które używały ref, ponieważ in nie były jeszcze dostępne),
  • modyfikowanie reguł rozwiązywania przeciążeń w celu dalszego poszukiwania lepszego dopasowania (określonego poniżej przez zasady określające lepsze dopasowanie) w przypadku niezgodności rodzaju referencji proponowanej w tej propozycji,
    • lub alternatywnie kontynuuj tylko w przypadku niezgodności ref vs. in, a nie z innymi (ref readonly vs. ref/in/według wartości).
Zasady lepszości

Poniższy przykład powoduje obecnie trzy błędy niejednoznaczności dla trzech wywołań M. Możemy dodać nowe reguły poprawy, aby rozwiązać niejednoznaczności. Spowoduje to również rozwiązanie opisanej wcześniej zmiany powodującej niekompatybilność źródła. Jednym ze sposobów byłoby sprawienie, że przykład wydrukuje 221 (gdzie parametr ref readonly jest zgodny z argumentem in, co oznacza, że wywołanie go bez modyfikatora byłoby ostrzeżeniem, podczas gdy dla parametru in jest to dozwolone).

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));
    }
}

Nowe zasady ulepszania mogą oznaczyć parametr jako gorszy, jeżeli jego argument mógłby zostać przekazany z innym modyfikatorem, aby go poprawić. Innymi słowy, użytkownik powinien zawsze mieć możliwość przekształcenia gorszego parametru w lepszy parametr, zmieniając odpowiedni modyfikator argumentów. Jeśli na przykład argument jest przekazywany przez in, preferowany jest parametr ref readonly dla parametru in, ponieważ użytkownik może przekazać argument by-value, aby wybrać parametr in. Ta reguła jest tylko rozszerzeniem reguły preferencji by-value/in, która jest obecnie obowiązują (jest to ostatnia reguła rozwiązywania przeciążenia i całe przeciążenie jest lepsze, jeśli którykolwiek z jego parametrów jest lepszy i żaden nie jest gorszy niż odpowiedni parametr innego przeciążenia).

kłótnia lepszy parametr gorszy parametr
ref/in ref readonly in
ref ref ref readonly/in
przez wartość by-value/in ref readonly
in in ref

Podobnie powinniśmy obsługiwać konwersje metod. W poniższym przykładzie obecnie występują dwa błędy niejednoznaczności dotyczące dwóch przypisań delegatów. Nowe reguły poprawy wydajności mogą preferować parametr metody, którego modyfikator refness pasuje do odpowiedniego docelowego modyfikatora refness parametru delegata na jeden, który ma niezgodność. W związku z tym poniższy przykład wyświetli 12.

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);

Spotkania projektowe