Jaki jest cel tego pozornego odniesienia w języku C #?

21

Oceniam CMS typu open source o nazwie Piranha ( http://piranhacms.org/ ) do wykorzystania w jednym z moich projektów. Dla mnie poniższy kod był interesujący i nieco mylący. Czy ktoś może mi pomóc zrozumieć, dlaczego klasa dziedziczy po bazie tego samego typu?

public abstract class BasePage<T> : Page<T> where T : BasePage<T>
{
    /// <summary>
    /// Gets/sets the page heading.
    /// </summary>
    [Region(SortOrder = 0)]
    public Regions.PageHeading Heading { get; set; }
}

Jeśli BasePage<T>definiowana jest klasa , po co dziedziczyć Page<T> where T: BasePage<T>? Jakiemu konkretnemu celowi to służy?

Xami Yen
źródło
7
Pomimo głosów negatywnych i bliskich głosów, myślę, że społeczność źle to zrozumiała. Jest to jasno określone pytanie dotyczące konkretnej i nietrywialnej decyzji projektowej, samej istoty tego, o czym jest ta strona.
Robert Harvey,
Po prostu poczekaj i otwórz go ponownie, gdy się zamknie.
David Arno,
Przeczytaj o koncepcji polimorfizmu związanego z
literą
1
@Eyvind faktycznie to zrobiłem. Dla osób zainteresowanych czytaniem o polimorfizmie F-Bounded, oto link staff.ustc.edu.cn/~xyfeng/teaching/FOPL/lectureNotes/…
Xami Yen

Odpowiedzi:

13

Czy ktoś może mi pomóc zrozumieć, dlaczego klasa dziedziczy po bazie tego samego typu?

Nie jest, dziedziczy po Page<T>, ale Tsam w sobie jest sparametryzowany przez typ, z którego pochodzi BasePage<T>.

Aby wywnioskować, dlaczego, musisz spojrzeć na to, jak Tfaktycznie używany jest parametr type . Po pewnym kopaniu, idąc w górę łańcucha spadkowego, natkniesz się na tę klasę:

( github )

public class GenericPage<T> : PageBase where T : GenericPage<T>
{
    public bool IsStartPage {
        get { return !ParentId.HasValue && SortOrder == 0; }
    }

    public GenericPage() : base() { }

    public static T Create(IApi api, string typeId = null)
    {
        return api.Pages.Create<T>(typeId);
    }
}

O ile widzę, jedynym celem ogólnego ograniczenia jest upewnienie się, że Createmetoda zwraca możliwie najmniej abstrakcyjny typ.

Nie jestem jednak pewien, czy warto, ale może jest za tym jakiś dobry powód lub może to być tylko dla wygody, a może nie kryje się za tym zbyt wiele substancji i to po prostu zbyt skomplikowany sposób na uniknięcie obsady (BTW, ja nie sugeruję, że tak jest w tym przypadku, mówię tylko, że ludzie czasem tak robią).

Należy pamiętać, że to nie pozwala im uniknąć refleksji - w api.Pagesto repozytorium stron, które uzyskuje typeof(T).Namei przekazuje go jako typeIddo contentService.Createmetody ( patrz tutaj ).

Filip Milovanović
źródło
5

Jedno z powszechnych zastosowań tego jest związane z koncepcją typów własnych: parametr typu, który jest rozwiązywany do bieżącego typu. Powiedzmy, że chcesz zdefiniować interfejs za pomocą clone()metody. clone()Metoda musi zawsze powrócić instancję klasy, w którym została wywołana. Jak deklarujesz tę metodę? W systemie generycznym, który ma własny typ, jest to łatwe. Po prostu mówisz, że powraca self. Więc jeśli mam klasę Foo, metoda klonowania musi zostać zadeklarowana jako zwracana Foo. W Javie i (z pobieżnego wyszukiwania) C # nie jest to opcja. Zamiast tego widzisz deklaracje podobne do tego, co widzisz w tej klasie. Ważne jest, aby zrozumieć, że nie jest to to samo, co samokreślenie, a ograniczenia, które zapewnia, są słabsze. Jeśli masz klasę Fooi, z Barktórej oba pochodząBasePage, możesz (jeśli się nie mylę) zdefiniować Foo do parametryzacji Bar. Może to być przydatne, ale myślę, że zazwyczaj przez większość czasu będzie to używane jako własny typ i zrozumiałe jest, że nawet jeśli możesz wygłupiać się i zastępować innymi typami, nie należy tego robić. Bawiłem się z tym pomysłem dawno temu, ale doszedłem do wniosku, że nie było to warte wysiłku, ponieważ ograniczenia generyczne Java. Generyczne C # są oczywiście w pełni funkcjonalne, ale wydaje się, że ma to samo ograniczenie.

Innym razem stosuje się to podejście, gdy budujesz typy wykresów, takie jak drzewa lub inne rekurencyjne struktury. Deklaracja pozwala typom spełniać wymagania strony, ale dodatkowo uściśla typ. Możesz to zobaczyć w strukturze drzewa. Na przykład a Nodemożna sparametryzować, Nodeaby umożliwić implementacjom zdefiniowanie, że nie są to tylko Drzewa zawierające dowolny typ Węzła, ale określony podtyp Węzła (zwykle ich własny typ). Myślę, że więcej o tym tu chodzi.

JimmyJames
źródło
3

Będąc osobą, która faktycznie napisała kod, mogę potwierdzić, że Filip jest poprawny, a ogólny odnośnik ogólny jest w rzeczywistości wygodą dla zapewnienia typowej metody Create w klasie podstawowej.

Jak wspomina, wciąż wiele się zastanawia, a na końcu tylko nazwa typu jest używana do rozwiązania typu strony. Powodem tego jest to, że można również ładować modele dynamiczne, tj. Zmaterializować model bez dostępu do typu CLR, który go utworzył.

Håkan Edling
źródło