Setelah gagal mendapatkan sesuatu seperti berikut untuk dikompilasi:
public class Gen<T> where T : System.Array
{
}
dengan kesalahan
Batasan tidak dapat berupa kelas khusus 'System.Array'
Aku mulai bertanya-tanya, apa sebenarnya adalah sebuah "kelas khusus"?
Orang-orang sering kali mendapatkan jenis kesalahan yang sama saat mereka menentukan System.Enum
dalam batasan umum. Saya mendapat hasil yang sama dengan System.Object
, System.Delegate
, System.MulticastDelegate
dan System.ValueType
juga.
Apakah masih ada lagi? Saya tidak dapat menemukan info tentang "kelas khusus" di C #.
Juga, apa yang istimewa dari kelas-kelas itu sehingga kita tidak dapat menggunakannya sebagai batasan tipe umum?
c#
class
generics
generic-constraints
Mints97
sumber
sumber
System.Object
adalah bukan "kelas khusus", karena ini adalah valid:public class X : System.Object { }
, tapiSystem.Object
masih "kelas khusus".Jawaban:
Dari kode sumber Roslyn, ini terlihat seperti daftar tipe hardcode:
Sumber: Binder_Constraints.cs IsValidConstraintType
Saya telah menemukannya menggunakan pencarian GitHub: "Batasan tidak bisa menjadi kelas khusus"
sumber
CS0702
.object
), atau setidaknya ada hubungannya dengan itu. Jugawhere T : Array
akan memungkinkan lulus Assay sebagai T, yang mungkin bukan yang diinginkan kebanyakan orang.Saya menemukan komentar Jon Skeet dari tahun 2008 tentang pertanyaan serupa: Mengapa
System.Enum
batasan tidak didukung.Saya tahu ini sedikit keluar dari topik , tetapi dia bertanya kepada Eric Lippert (tim C #) tentang hal itu dan mereka memberikan jawaban ini:
sumber
Menurut MSDN , ini adalah daftar kelas statis:
Kesalahan Penyusun CS0702
Batasan tidak dapat menjadi 'pengenal' kelas khusus. Jenis berikut tidak dapat digunakan sebagai pembatas:
sumber
System.MulticastDelegate
di manakah daftarnya?Sesuai C # 4.0 Spesifikasi Bahasa (Berkode: [10.1.5] Batasan parameter tipe) memberi tahu dua hal:
Saat Anda mendefinisikan kelas generik, Anda bisa menerapkan batasan pada jenis tipe yang dapat digunakan kode klien untuk argumen tipe saat membuat instance kelas Anda. Jika kode klien mencoba untuk membuat instance kelas Anda dengan menggunakan tipe yang tidak diperbolehkan oleh batasan, hasilnya adalah kesalahan waktu kompilasi. Pembatasan ini disebut kendala. Batasan ditentukan dengan menggunakan kata kunci kontekstual where. Jika Anda ingin membatasi tipe generik menjadi tipe referensi, gunakan: class.
Ini akan melarang tipe generik menjadi tipe nilai, seperti int atau struct, dll.
Selain itu, Batasan tidak dapat menjadi 'pengenal' kelas khusus. Jenis berikut tidak dapat digunakan sebagai pembatas:
sumber
Ada kelas-kelas tertentu dalam Framework yang secara efektif meneruskan karakteristik khusus ke semua tipe yang diturunkan darinya tetapi tidak memiliki karakteristik itu sendiri . CLR sendiri tidak memberlakukan larangan untuk menggunakan kelas-kelas tersebut sebagai batasan, tetapi tipe generik yang dibatasi padanya tidak akan memperoleh karakteristik yang tidak diturunkan seperti tipe konkret. Pencipta C # memutuskan bahwa karena perilaku seperti itu mungkin membingungkan sebagian orang, dan mereka gagal melihat kegunaannya, mereka harus melarang batasan tersebut daripada membiarkan mereka berperilaku seperti yang mereka lakukan di CLR.
Jika, misalnya, seseorang diizinkan untuk menulis
void CopyArray<T>(T dest, T source, int start, int count)
:; seseorang akan dapat melewatkandest
dansource
ke metode yang mengharapkan sebuah argumen bertipeSystem.Array
; selanjutnya, seseorang akan mendapatkan validasi waktu kompilasi itudest
dansource
merupakan tipe array yang kompatibel, tetapi seseorang tidak akan bisa mengakses elemen dari array menggunakan[]
operator.Ketidakmampuan untuk menggunakan
Array
sebagai kendala sebagian besar cukup mudah untuk dikerjakan, karenavoid CopyArray<T>(T[] dest, T[] source, int start, int count)
akan bekerja di hampir semua situasi di mana metode sebelumnya akan bekerja. Namun, ia memiliki kelemahan: metode sebelumnya akan bekerja dalam skenario bahwa salah satu atau kedua argumen bertipeSystem.Array
sementara menolak kasus di mana argumen merupakan jenis array yang tidak kompatibel; menambahkan overload di mana kedua argumen memiliki tipeSystem.Array
akan membuat kode menerima kasus tambahan yang harus diterimanya, tetapi juga membuatnya salah menerima kasus yang seharusnya tidak diterima.Menurut saya keputusan untuk melarang sebagian besar batasan khusus menjengkelkan. Satu-satunya yang memiliki makna semantik nol adalah
System.Object
[karena jika itu legal sebagai batasan, apa pun akan memuaskannya].System.ValueType
mungkin tidak akan terlalu berguna, karena referensi tipeValueType
tidak memiliki banyak kesamaan dengan tipe nilai, tetapi mungkin masuk akal memiliki beberapa nilai dalam kasus yang melibatkan Refleksi. KeduanyaSystem.Enum
danSystem.Delegate
akan memiliki kegunaan nyata, tetapi karena pencipta C # tidak memikirkannya, mereka dilarang tanpa alasan yang jelas.sumber
Berikut ini dapat ditemukan di CLR melalui C # 4th Edition:
Kendala Utama
Parameter tipe dapat menentukan batasan utama nol atau satu batasan utama. Batasan utama bisa menjadi tipe referensi yang mengidentifikasi kelas yang tidak disegel. Anda tidak dapat menentukan salah satu dari jenis referensi khusus berikut: System.Object , System.Array , System.Delegate , System.MulticastDelegate , System.ValueType , System.Enum , atau System.Void . Saat menentukan batasan tipe referensi, Anda menjanjikan compiler bahwa argumen tipe yang ditentukan akan berjenis sama atau tipe turunan dari tipe batasan.
sumber
System.Array
,System.Delegate
,System.MulticastDelegate
,System.Enum
, atauSystem.ValueType
. Selanjutnya, deklarasi kelas generik tidak dapat digunakanSystem.Attribute
sebagai kelas dasar langsung atau tidak langsung.Saya tidak berpikir, bahwa ada definisi resmi dari "kelas khusus" / "tipe khusus".
Anda mungkin menganggapnya sebagai jenis, yang tidak dapat digunakan dengan semantik jenis "biasa":
PS Saya akan menambahkan
System.Void
ke daftar.sumber
System.Void
memberikan kesalahan yang sama sekali berbeda bila digunakan sebagai batasan umum =)void
sangat istimewa. :)System.Array
dapat menggunakan metode sepertiArray.Copy
memindahkan data dari satu ke yang lain; kode dengan parameter dari tipe yang dibatasiSystem.Delegate
akan dapat digunakanDelegate.Combine
padanya dan menampilkan hasilnya ke tipe yang tepat . Memanfaatkan jenis yang diketahui umum secara efektifEnum
akan menggunakan Refleksi satu kali untuk setiap jenis tersebut, tetapiHasAnyFlag
metode umum bisa 10x lebih cepat daripada metode non-umum.