Rozszerzanie metod częściowych
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 .
Streszczenie
Wniosek ten ma na celu usunięcie wszystkich ograniczeń dotyczących podpisów metod partial
w języku C#. Celem jest rozszerzenie zestawu scenariuszy, w których te metody mogą współpracować z generatorami źródłowymi, a także bardziej ogólną formą deklaracji dla metod języka C#.
Zobacz również pierwotną specyfikację metod częściowych (§15.6.9).
Motywacja
Język C# ma ograniczoną obsługę deweloperów dzielących metody na deklaracje i definicje /implementacje.
partial class C
{
// The declaration of C.M
partial void M(string message);
}
partial class C
{
// The definition of C.M
partial void M(string message) => Console.WriteLine(message);
}
Jednym z zachowań metod partial
jest to, że gdy definicja jest nieobecna, język po prostu wymazuje wszystkie wywołania metody partial
. Zasadniczo zachowuje się jak wywołanie metody [Conditional]
, w której warunek został oceniony na wartość false.
partial class D
{
partial void M(string message);
void Example()
{
M(GetIt()); // Call to M and GetIt erased at compile time
}
string GetIt() => "Hello World";
}
Oryginalną motywacją dla tej funkcji było generowanie źródła w postaci wygenerowanego kodu projektanta. Użytkownicy stale edytowali wygenerowany kod, ponieważ chcieli podłączyć jakiś aspekt wygenerowanego kodu. W szczególności części procesu uruchamiania formularzy systemu Windows po zainicjowaniu składników.
Edytowanie wygenerowanego kodu było podatne na błędy, ponieważ każda akcja, która spowodowała ponowne wygenerowanie kodu przez projektanta, spowoduje usunięcie edycji użytkownika. Metoda partial
złagodziła to napięcie, ponieważ pozwoliła projektantom emitować haki w postaci metod partial
.
Projektanci mogą emitować haki, takie jak partial void OnComponentInit()
, a deweloperzy mogą albo definiować dla nich deklaracje, albo nie definiować ich wcale. W każdym przypadku wygenerowany kod skompilowałby, a deweloperzy, którzy byli zainteresowani tym procesem, mogli się podłączyć, kiedy było to potrzebne.
Oznacza to, że metody częściowe mają kilka ograniczeń:
- Musi mieć typ zwracany
void
. - Nie można używać parametrów
out
. - Nie można uzyskać dostępu (niejawnie
private
).
Te ograniczenia istnieją, ponieważ język musi być w stanie emitować kod po wymazaniu lokacji wywołania. Biorąc pod uwagę możliwość ich wymazania, private
jest jedyną możliwą opcją dostępu, ponieważ członek nie może być ujawniony w metadanych asemblera. Te ograniczenia służą również do ograniczania zestawu scenariuszy, w których można zastosować metody partial
.
Wniosek ten polega na usunięciu wszystkich istniejących ograniczeń dotyczących metod partial
. Zasadniczo pozwól im mieć out
parametry, niepuste typy zwracania lub dowolny poziom dostępu. Takie partial
deklaracje miałyby wówczas dodatkowe wymaganie, aby definicja musiała istnieć. Oznacza to, że język nie musi uwzględniać wpływu wymazywania miejsc wywołań.
Rozszerzyłoby to zestaw scenariuszy dla generatora, w których partial
metody mogą uczestniczyć, i tym samym lepiej integrowałoby się z naszą funkcją generatorów źródłowych. Na przykład wyrażenie regularne można zdefiniować przy użyciu następującego wzorca:
[RegexGenerated("(dog|cat|fish)")]
partial bool IsPetMatch(string input);
Daje to zarówno deweloperowi prosty deklaratywny sposób włączania generatorów, jak i umożliwia generatorom łatwe przeglądanie deklaracji w kodzie źródłowym, co wspomaga tworzenie wygenerowanego wyjścia.
Porównaj to z trudnością, z jaką generator miałby podłączyć następujący fragment kodu.
var regex = new RegularExpression("(dog|cat|fish)");
if (regex.IsMatch(someInput))
{
}
Biorąc pod uwagę, że kompilator nie zezwala generatorom na modyfikowanie kodu podłączania tego wzorca byłoby prawie niemożliwe dla generatorów. Musieliby sięgnąć po refleksję w implementacji IsMatch
lub poprosić użytkowników o zmianę miejsc wywołań na nową metodę oraz o refaktoryzację wyrażenia regularnego, aby przekazać literał ciągu jako argument. To dość niechlujne.
Szczegółowy projekt
Język zmieni się tak, aby zezwolić na dodawanie adnotacji do metod partial
za pomocą jawnego modyfikatora ułatwień dostępu. Oznacza to, że można je oznaczyć jako private
, public
itp.
Jeśli metoda partial
ma jawny modyfikator ułatwień dostępu, język będzie wymagał, aby deklaracja zawiera zgodną definicję, nawet jeśli ułatwienia dostępu są private
:
partial class C
{
// Okay because no definition is required here
partial void M1();
// Okay because M2 has a definition
private partial void M2();
// Error: partial method M3 must have a definition
private partial void M3();
}
partial class C
{
private partial void M2() { }
}
Ponadto język usunie wszystkie ograniczenia dotyczące tego, co może pojawić się w metodzie partial
, która ma jawną dostępność. Takie deklaracje mogą zawierać niepuste typy zwracane, parametry out
, modyfikator extern
itp. Te sygnatury będą miały pełną wyrazistość języka C#.
partial class D
{
// Okay
internal partial bool TryParse(string s, out int i);
}
partial class D
{
internal partial bool TryParse(string s, out int i) { ... }
}
To jawnie pozwala na udział metod partial
w implementacjach overrides
i interface
.
interface IStudent
{
string GetName();
}
partial class C : IStudent
{
public virtual partial string GetName();
}
partial class C
{
public virtual partial string GetName() => "Jarde";
}
Kompilator zmieni błąd, który emituje, gdy metoda partial
zawiera niedozwolony element, aby zasadniczo powiedzieć:
Nie można użyć
ref
w metodziepartial
, która nie ma jawnego zakresu dostępu
Pomoże to wskazać deweloperom właściwy kierunek korzystania z tej funkcji.
Ograniczenia:
-
partial
deklaracje z jawnymi ułatwieniami dostępu muszą mieć definicję -
partial
deklaracje i sygnatury definicji muszą być takie same we wszystkich modyfikatorach metod i parametrów. Jedynymi aspektami, które mogą się różnić, są nazwy parametrów i listy atrybutów (nie jest to nowe, ale raczej istniejące wymaganiepartial
metod).
Pytania
częściowe u wszystkich członków
Biorąc pod uwagę, że rozszerzamy partial
, aby był bardziej przyjazny dla generatorów źródłowych, czy powinniśmy również rozszerzyć go, aby działał na wszystkich członkach klasy? Na przykład powinniśmy mieć możliwość deklarowania partial
konstruktorów, operatorów itp.
Resolution Pomysł jest poprawny, ale na tym etapie harmonogramu języka C# 9 staramy się uniknąć niepotrzebnego rozrostu funkcji. Chcesz rozwiązać bezpośredni problem rozszerzania funkcji do pracy z nowoczesnymi generatorami źródeł.
Rozszerzenie partial
na obsługę innych członków zostanie uwzględnione w wersji C# 10. Wydaje się prawdopodobne, że rozważymy to rozszerzenie. Pozostaje to aktywną propozycją, ale nie została jeszcze wdrożona.
Używanie abstrakcji zamiast częściowych
Podstawą niniejszego wniosku jest zasadniczo zapewnienie, że deklaracja ma odpowiednią definicję/implementację. Czy powinniśmy użyć abstract
, skoro jest to słowo kluczowe języka, które zmusza programistę do myślenia o implementacji?
rezolucji Była zdrowa dyskusja na ten temat, ale ostatecznie zdecydowano się przeciwko. Tak, wymagania są znane, ale koncepcje są znacznie różne. Łatwo może prowadzić dewelopera do przekonania, że tworzy wirtualne automaty do gry, gdy w rzeczywistości tego nie robi.
C# feature specifications