Saya mencoba membuat objek tipe T baru melalui konstruktornya saat menambahkan ke daftar.
Saya mendapatkan kesalahan kompilasi: Pesan kesalahan adalah:
'T': tidak dapat memberikan argumen saat membuat turunan variabel
Tetapi kelas saya memang memiliki argumen konstruktor! Bagaimana saya bisa membuat ini berfungsi?
public static string GetAllItems<T>(...) where T : new()
{
...
List<T> tabListItems = new List<T>();
foreach (ListItem listItem in listCollection)
{
tabListItems.Add(new T(listItem)); // error here.
}
...
}
Jawaban:
Untuk membuat turunan tipe generik dalam suatu fungsi, Anda harus membatasi dengan flag "baru".
Namun itu hanya akan berfungsi ketika Anda ingin memanggil konstruktor yang tidak memiliki parameter. Tidak demikian di sini. Sebaliknya Anda harus memberikan parameter lain yang memungkinkan untuk membuat objek berdasarkan parameter. Yang paling mudah adalah fungsi.
Anda kemudian dapat menyebutnya seperti itu
sumber
T result = thunk == null ? new T() : thunk();
Manfaat ini bagi saya adalah mengkonsolidasikan logikaT
penciptaan di satu tempat daripada terkadang membuatT
di dalam dan kadang-kadang di luar metode.di .Net 3.5 dan setelah Anda bisa menggunakan kelas aktivator:
sumber
Activator.CreateInstance
untuk satu hal ini, itu akan terlihat seperti konstruktor Anda tidak digunakan sama sekali, dan seseorang mungkin mencoba "membersihkan" dan menghapusnya (untuk menyebabkan kesalahan runtime di beberapa waktu acak di masa depan). Anda mungkin ingin mempertimbangkan untuk menambahkan fungsi dummy tempat Anda menggunakan konstruktor ini sehingga Anda mendapatkan kesalahan kompilasi jika Anda mencoba menghapusnya.Karena tidak ada yang mau memposting jawaban 'Refleksi' (yang menurut saya pribadi adalah jawaban terbaik), begini:
Sunting: Jawaban ini sudah usang karena Activator.CreateInstance .NET 3.5, namun masih berguna dalam versi .NET yang lebih lama.
sumber
Penginisialisasi objek
Jika konstruktor Anda dengan parameter tidak melakukan apa pun selain mengatur properti, Anda dapat melakukan ini di C # 3 atau lebih baik menggunakan penginisialisasi objek daripada memanggil konstruktor (yang tidak mungkin, seperti yang telah disebutkan):
Dengan menggunakan ini, Anda selalu dapat memasukkan logika konstruktor di konstruktor default (kosong) juga.
Activator.CreateInstance ()
Atau, Anda dapat memanggil Activator.CreateInstance () seperti:
Perhatikan bahwa Activator.CreateInstance dapat memiliki beberapa overhead kinerja yang mungkin ingin Anda hindari jika kecepatan eksekusi adalah prioritas utama dan opsi lain dapat dipertahankan untuk Anda.
sumber
T
dari melindungi invariannya (mengingat yangT
memiliki> 0 dependensi atau nilai yang diperlukan, Anda sekarang dapat membuat instanceT
yang dalam keadaan tidak valid / tidak dapat digunakan. kecualiT
ada sesuatu yang mati sederhana seperti model tampilan DTO dan saya akan mengatakan hindari ini.Pertanyaan yang sangat lama, tetapi jawaban baru ;-)
Versi ExpressionTree : (Saya pikir solusi tercepat dan terbersih)
Seperti kata Welly Tambunan , "kita juga bisa menggunakan pohon ekspresi untuk membangun objek"
Ini akan menghasilkan 'konstruktor' (fungsi) untuk jenis / parameter yang diberikan. Ini mengembalikan delegasi dan menerima tipe parameter sebagai array objek.
Ini dia:
Contoh MyClass:
Pemakaian:
Contoh lain: melewatkan tipe sebagai array
DebugView dari Ekspresi
Ini sama dengan kode yang dihasilkan:
Kelemahan kecil
Semua parameter valuetipe kotak ketika mereka dilewatkan seperti array objek.
Tes kinerja sederhana:
Hasil:
Menggunakan
Expressions
+/- 8 kali lebih cepat daripada MemanggilConstructorInfo
dan +/- 20 kali lebih cepat daripada menggunakanActivator
sumber
var myConstructor = CreateConstructor(typeof(MyClass<int>), typeof(int));
ini berfungsi dengan baik. Saya tidak tahuIni tidak akan berfungsi dalam situasi Anda. Anda hanya dapat menentukan batasan yang memiliki konstruktor kosong:
Apa yang bisa Anda lakukan adalah menggunakan injeksi properti dengan mendefinisikan antarmuka ini:
Maka Anda dapat mengubah metode Anda menjadi ini:
Alternatif lain adalah
Func
metode yang dijelaskan oleh JaredPar.sumber
Anda perlu menambahkan di mana T: new () agar kompiler tahu bahwa T dijamin untuk menyediakan konstruktor default.
sumber
Jika Anda hanya ingin menginisialisasi bidang anggota atau properti dengan parameter konstruktor, dalam C #> = 3 Anda dapat melakukannya dengan lebih mudah:
Ini adalah hal yang sama yang dikatakan Garry Shutler, tetapi saya ingin memberikan catatan tambahan.
Tentu saja Anda dapat menggunakan trik properti untuk melakukan lebih banyak hal daripada sekadar menetapkan nilai bidang. Properti "set ()" dapat memicu pemrosesan yang diperlukan untuk mensetup bidang terkait dan kebutuhan lain untuk objek itu sendiri, termasuk pemeriksaan untuk melihat apakah inisialisasi penuh akan terjadi sebelum objek digunakan, mensimulasikan konstruksi penuh ( ya, ini adalah solusi buruk, tetapi mengatasi batasan M $ baru ()).
Saya tidak dapat memastikan apakah itu lubang yang direncanakan atau efek samping yang tidak disengaja, tetapi berhasil.
Sangat lucu bagaimana orang MS menambahkan fitur baru ke bahasa dan tampaknya tidak melakukan analisis efek samping penuh. Seluruh hal generik adalah bukti yang baik dari ini ...
sumber
Saya menemukan bahwa saya mendapatkan kesalahan "tidak dapat memberikan argumen saat membuat turunan dari tipe parameter T" jadi saya perlu melakukan ini:
sumber
Jika Anda memiliki akses ke kelas yang akan Anda gunakan, Anda dapat menggunakan pendekatan ini yang saya gunakan.
Buat antarmuka yang memiliki pencipta alternatif:
Buat kelas Anda dengan pembuat kosong dan terapkan metode ini:
Sekarang gunakan metode generik Anda:
Jika Anda tidak memiliki akses, bungkus kelas target:
sumber
Ini adalah jenis mucky, dan ketika saya mengatakan jenis mucky mungkin maksud saya memberontak, tetapi seandainya Anda dapat melengkapi tipe parameterised Anda dengan konstruktor kosong, maka:
Secara efektif akan memungkinkan Anda untuk membangun objek dari tipe parameter dengan argumen. Dalam hal ini saya mengasumsikan konstruktor yang saya inginkan memiliki argumen tipe tunggal
object
. Kami membuat contoh tiruan dari T menggunakan konstruktor kosong yang diizinkan dan kemudian menggunakan refleksi untuk mendapatkan salah satu konstruktor lainnya.sumber
Saya terkadang menggunakan pendekatan yang mirip dengan jawaban menggunakan injeksi properti, tetapi menjaga kode tetap bersih. Alih-alih memiliki kelas dasar / antarmuka dengan sekumpulan properti, ia hanya berisi inisialisasi (virtual) () - metode yang bertindak sebagai "konstruktor orang miskin". Kemudian Anda dapat membiarkan setiap kelas menangani inisialisasi sendiri seperti yang dilakukan oleh konstruktor, yang juga menambahkan cara yang nyaman untuk menangani rantai pewarisan.
Jika sering menemukan diri saya dalam situasi di mana saya ingin setiap kelas dalam rantai untuk menginisialisasi properti uniknya, dan kemudian memanggil metode Initialize () - induknya yang pada gilirannya menginisialisasi properti unik orangtua dan sebagainya. Ini sangat berguna ketika memiliki kelas yang berbeda, tetapi dengan hierarki yang sama, misalnya objek bisnis yang dipetakan ke / dari DTO: s.
Contoh yang menggunakan Kamus umum untuk inisialisasi:
sumber
Jika semua yang Anda butuhkan adalah konversi dari ListItem ke tipe T Anda, Anda dapat mengimplementasikan konversi ini di kelas T sebagai operator konversi.
sumber
Saya percaya Anda harus membatasi T dengan pernyataan di mana hanya mengizinkan objek dengan konstruktor baru.
HANYA sekarang ia menerima apa saja termasuk benda tanpa itu.
sumber