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ępnieniemin
, a zmiana nain
będzie zmianą łamiącą zgodność na poziomie źródła i binariów, np.QueryInterface
lub -
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
-
ref
→ref readonly
byłaby tylko zmianą binarną powodującą niekompatybilność dla metod wirtualnych. -
ref
→in
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 argumentamiref
przekazanymi do parametrówin
), -
in
→ref 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 parametryref
, co uniemożliwiain
lub brak adnotacji w wywołaniach) oraz nowe wersje kompilatora zLangVersion <= 11
(dla zapewnienia spójności ze starszymi wersjami kompilatora, zostanie zgłoszony błąd informujący, że parametryref readonly
nie są obsługiwane, chyba że odpowiednie argumenty są przekazywane z modyfikatoremref
).
- 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
W przeciwnym kierunku następuje zmiana
-
ref readonly
→ref
byłaby potencjalnie zmianą powodującą niezgodność źródła (chyba że użyto tylko adnotacji miejsca wywołaniaref
i użyto jedynie odwołań tylko do odczytu jako argumentów) oraz zmianą powodującą niezgodność binarną dla metod wirtualnych. -
ref readonly
→in
nie byłby zmianą powodującą niezgodność (ale adnotacja miejsc wywołaniaref
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 parametremin
lubref
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 dopasowaniain
ani parametruref 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 doref
oraz są oznaczone adnotacjami zSystem.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 ref
→ ref 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 in
→ ref 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 |
ref → ref readonly |
in → ref 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 in
→ ref readonly
nie byłaby zmianą naruszającą kompatybilność nawet w przypadku starszych wersji kompilatora, ale ref
→ ref 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ę doin
dla starych interfejsów API, które używałyref
, 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).
- lub alternatywnie kontynuuj tylko w przypadku niezgodnoś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
- LDM 2022-04-25: zaakceptowana funkcja
- LDM 2022-05-09: dyskusja podzielona na trzy części
-
LDM 2022-05-11: dozwolone
ref
i brak adnotacji miejsca wywołania dla parametrówref readonly
C# feature specifications