Udostępnij za pośrednictwem


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ń:

  1. Musi mieć typ zwracany void.
  2. Nie można używać parametrów out.
  3. 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, publicitp.

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 metodzie partial, 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 wymaganie partial 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.