Apa tujuan dari referensi diri ini dalam C #?

21

Saya mengevaluasi CMS open source yang disebut Piranha ( http://piranhacms.org/ ) untuk digunakan dalam salah satu proyek saya. Saya menemukan kode berikut ini menarik dan agak membingungkan, setidaknya bagi saya. Dapatkah beberapa orang membantu saya memahami mengapa kelas mewarisi dari jenis yang sama?

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

Jika kelas BasePage<T>sedang didefinisikan, mengapa mewarisi dari Page<T> where T: BasePage<T>? Apa tujuan spesifik yang dilayaninya?

Xami Yen
sumber
7
Terlepas dari downvotes dan suara dekat, saya pikir masyarakat salah dalam hal ini. Ini adalah pertanyaan yang dinyatakan dengan jelas berkaitan dengan keputusan desain yang spesifik dan non-sepele, inti dari situs ini.
Robert Harvey
Hanya berkeliaran dan buka kembali ketika sudah ditutup.
David Arno
Bacalah konsep polimorfisme terikat-F :)
Eyvind
1
@Eyvind sebenarnya saya lakukan. Bagi mereka yang tertarik membaca tentang polimorfisme F-Bounded, di sini ada tautan staff.ustc.edu.cn/~xyfeng/teaching/FOPL/lectureNotes/…
Xami Yen

Jawaban:

13

Dapatkah beberapa orang membantu saya memahami mengapa kelas mewarisi dari jenis yang sama?

Bukan, ini merupakan turunan dari Page<T>, tetapi Titu sendiri dibatasi untuk menjadi parameter oleh tipe yang diturunkan BasePage<T>.

Untuk menyimpulkan mengapa, Anda harus melihat bagaimana parameter tipe Tsebenarnya digunakan. Setelah beberapa penggalian, saat Anda naik ke rantai warisan, Anda akan menemukan kelas ini:

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

Sejauh yang saya bisa lihat, satu-satunya tujuan untuk batasan generik adalah untuk memastikan bahwa Createmetode mengembalikan tipe abstrak paling tidak mungkin.

Tidak yakin apakah itu sepadan, tapi mungkin ada beberapa alasan bagus di baliknya, atau bisa saja untuk kenyamanan, atau mungkin tidak ada terlalu banyak zat di baliknya dan itu hanya cara yang terlalu rumit untuk menghindari gips (BTW, saya Saya tidak menyiratkan bahwa inilah yang terjadi di sini, saya hanya mengatakan bahwa kadang-kadang orang melakukan itu).

Perhatikan bahwa ini tidak memungkinkan mereka untuk menghindari refleksi - yang api.Pagesadalah gudang halaman yang memperoleh typeof(T).Name, dan dibagikan sebagai typeIdke contentService.Createmetode ( lihat di sini ).

Filip Milovanović
sumber
5

Salah satu penggunaan umum ini terkait dengan konsep tipe diri: parameter tipe yang memutuskan untuk tipe saat ini. Katakanlah Anda ingin mendefinisikan antarmuka dengan clone()metode. The clone()Metode harus selalu mengembalikan sebuah instance dari kelas yang itu disebut. Bagaimana Anda mendeklarasikan metode itu? Dalam sistem generik yang memiliki tipe mandiri, mudah. Anda hanya mengatakan itu kembali self. Jadi jika saya memiliki kelas Foo, metode klon harus dinyatakan untuk kembali Foo. Di Jawa dan (dari pencarian sepintas) C #, ini bukan pilihan. Anda malah melihat deklarasi seperti apa yang Anda lihat di kelas ini. Penting untuk dipahami bahwa ini bukan hal yang sama dengan tipe diri dan batasan yang diberikannya lebih lemah. Jika Anda memiliki Foodan Barkelas yang keduanya berasalBasePage, Anda dapat (jika saya tidak salah) mendefinisikan Foo untuk menjadi parameter oleh Bar. Itu mungkin berguna tapi saya pikir biasanya, sebagian besar waktu, ini akan digunakan seperti tipe diri dan itu hanya dipahami bahwa meskipun Anda dapat berkeliling dan mengganti dengan tipe lain, itu bukan sesuatu yang harus Anda lakukan. Saya bermain-main dengan ide ini sejak lama tetapi sampai pada kesimpulan bahwa itu tidak sepadan dengan usaha karena keterbatasan generik Java. C # generics tentu saja lebih lengkap tetapi tampaknya memiliki batasan yang sama.

Lain waktu pendekatan ini digunakan adalah ketika Anda sedang membangun tipe seperti grafik seperti pohon atau struktur rekursif lainnya. Deklarasi memungkinkan jenis untuk memenuhi persyaratan Halaman tetapi lebih lanjut jenis. Anda mungkin melihat ini di struktur pohon. Sebagai contoh, Nodemungkin parameter dengan Nodememungkinkan untuk implementasi untuk menentukan bahwa mereka bukan hanya Pohon yang mengandung jenis Node tetapi sub-jenis Node tertentu (biasanya tipe mereka sendiri.) Saya pikir itu lebih dari apa yang terjadi di sini.

JimmyJames
sumber
3

Menjadi orang yang benar-benar menulis kode saya dapat mengkonfirmasi bahwa Filip benar dan generik referensi diri sebenarnya kenyamanan untuk menyediakan metode Buat diketik pada kelas dasar.

Seperti yang dia sebutkan, masih ada banyak refleksi yang terjadi, dan pada akhirnya hanya nama tipe yang digunakan untuk menyelesaikan tipe halaman. Alasan untuk ini adalah bahwa Anda dapat memuat model dinamis juga, yaitu mewujudkan model tanpa memiliki akses ke tipe CLR yang membuatnya di tempat pertama.

Håkan Edling
sumber