Bagaimana cara membuat (misalnya) singleton secara otomatis dengan c# menggunakan CodeDOM/T4/PostSharp/apa pun?

Saya mencari cara untuk menghasilkan (misalnya) singleton secara otomatis, seperti:

[SingletonPatternAttribute]
public class Logger {
    public void Log (string txt) { /* do logging... */ }
}

sebagai upaya untuk menghilangkan boilerplate.

Adakah yang tahu bagaimana hal itu bisa dicapai? Saya tahu ada CodeDOM, Reflection.Emit, T4 dan seterusnya. Juga - ada (terutama) PostSharp, tapi saya akan sangat senang melihat solusi nyata untuk tantangan di atas.

Saya dapat melakukan pembuatan kode di konstruktor, misalnya, tetapi waktu kompilasi jelas jauh lebih baik.

Sunting:

Masalahnya di sini bukan Singleton, tetapi pemrograman generatif/meta di C# - cara membuat kode/menghilangkan boiler-plate dengan cara terbaik - contoh konkret apa yang ada?


person Tar    schedule 18.03.2013    source sumber
comment
Untuk masalah kelas ini (bukan singleton, tetapi yang lebih kompleks.., tetapi jika saya memahaminya dengan benar, singleton hanyalah sebuah contoh) Saya menggunakan banyak hal di masa lalu, dari Reflection.Emit hingga IKVM (membuat hidup lebih mudah) hingga MonoCSharp. Itu tergantung, tetapi ketika menggunakan atribut saya suka alternatif Refleksi/IKVM, menghasilkan rakitan sebagai langkah pasca-pembangunan   -  person Lorenzo Dematté    schedule 18.03.2013
comment
Tapi saya tertarik mendengar pendekatan yang berbeda, jadi.. +1 :)   -  person Lorenzo Dematté    schedule 18.03.2013
comment
@ dema80 - tetapi di mana Anda akan meletakkan pembuatan kode sebenarnya menggunakan Reflection.Emit ? di konstruktor class atau di tempat lain?   -  person Tar    schedule 18.03.2013
comment
pendekatan saya adalah membangun executable terpisah (proyek terpisah, untuk lebih jelasnya) yang mengambil rakitan asli (baru saja dikompilasi), menelusurinya, menemukan kelas dengan atribut dan membangun versi rakitan yang dihias. Namun target yang saya miliki sedikit berbeda, dan dalam kasus Anda, pendekatan AOP (PostSharp) bisa lebih masuk akal!   -  person Lorenzo Dematté    schedule 18.03.2013
comment
Saya juga menggunakan T4 pada proyek lain. Semua teknik ini membantu dalam beberapa hal pemrograman meta, tetapi teknik tersebut tidak (atau setidaknya, saya tidak dapat) membawanya ke tingkat yang Anda sebutkan: berdasarkan atribut dan kode sumber C# yang sebenarnya, ubah kode C#. Tidak mudah, dan menarik: Saya ingin tahu bagaimana orang lain melakukannya (tanpa menulis kompiler C#-›C# yang sebenarnya, MonoCsharp atau Roslyn)   -  person Lorenzo Dematté    schedule 18.03.2013


Jawaban (2)


Saya melakukan hal yang tepat untuk kelas logging saya, menggunakan wadah IOC dan menambahkan cakupan saat memetakan/mengikat objek. Misalnya menggunakan Ninject, pengikatannya adalah:

Bind<ILogger>().To<Logger>().InSingletonScope();

Kemudian di kelas saya di mana saya ingin menggunakan singleton logger, saya bisa menggunakan injeksi properti:

[Inject]
public ILogger Logger { get; set; }
person Adrian Thompson Phillips    schedule 18.03.2013
comment
Saya tidak mengerti. Saya tidak tahu Ninject, tapi apa yang menghalangi saya untuk mendefinisikan logger lain yang seharusnya unik? seperti: public ILogger AnotherLogger { get { return new Logger; } } - person Tar; 19.03.2013
comment
Anda dapat melakukan itu dan Anda akan mendapatkan contoh lain. Menggunakan teknik ini tidak menjamin bahwa instance lain tidak dapat dibuat jika pengembang dalam tim tidak mengetahui IOC digunakan untuk tujuan ini. - person Adrian Thompson Phillips; 20.03.2013

Beberapa waktu telah berlalu, namun saya belum membaca tanggapan yang memuaskan, jadi saya akan menuliskan apa yang telah saya lakukan di masa lalu. Saya juga masih sangat tertarik dengan pendekatan lain, karena saya merasa apa yang telah saya capai belum ideal dan saya sendiri ingin melakukan yang lebih baik.

Saya telah mencoba, dalam beberapa tahun terakhir, tiga dari empat pendekatan yang terdaftar untuk pembuatan kode kelas "yang diperkaya":

  • Refleksi. Memancarkan
  • T4
  • CodeDom (tapi saya membuang ini lebih awal, karena pada saat proyek saya, belum selesai!)

ditambah tiga lainnya:

  • IL menulis ulang menggunakan Profiler API Di Sini dan di sini (sebenarnya digunakan)
  • Intersepsi menggunakan ContextBoundObject Di sini (hanya diuji)
  • Menyematkan kompiler C# (Roslyn/MonoCSharp) Di sini dan sekarang juga scriptcs (hanya diteliti, tidak benar-benar digunakan)

Secara umum, saya menyelidiki semua alat ini untuk mendapatkan beberapa "jalinan" kode, dengan cara AOP, dimulai dari sesuatu yang mirip dengan contoh OP: membuat kelas dan metode ditandai dengan atribut "khusus", mengubah perilakunya. Pada saat proyek pertama saya, solusi AOP untuk C# belum cukup matang (tapi ini mungkin sudah berubah), jadi saya tidak bisa mengatakan apa pun tentang PostSharp, misalnya. Tujuan saya adalah, seperti yang saya katakan, sangat mirip: berdasarkan atribut pada kelas dan metode, menambahkan beberapa kode tambahan (dan juga anggota kelas) ke kelas. Saya mengakhiri menggunakan Reflection.Emit untuk prototipe, dan IL menulis ulang menggunakan Profiler API untuk hal terakhir.

Proyek kedua, yang akhirnya saya gunakan T4, sedikit berbeda; itu sudah menggunakan pembuat kode (lebih khusus lagi, generator parser), tetapi kami memerlukan modifikasi lebih lanjut (otomatis!) dari kode sumber yang dihasilkan.

Refleksi. Memancarkan

Saya menggunakan pendekatan berikut: sebuah kelas (sebut saja MakeProxy) menjalankan rakitan tertentu, mencari atribut, dan kemudian mengeluarkan dll baru ("proxy") yang disebut rakitan asli, menyediakan antarmuka dan perilaku yang diinginkan kepada pengguna.

Saya sebenarnya menggunakan kelas dalam sebuah alat, .NET yang dapat dieksekusi, yang dijalankan oleh MS Build setelah kompilasi berhasil.

Kerugian utama: referensi mungkin sulit untuk ditangani. Anda perlu mereferensikan dll yang dibuat secara otomatis, jadi Anda harus membagi kode Anda ke dalam proyek yang berbeda, dan Anda bahkan tidak dapat mereferensikan proyek asli dalam suatu solusi... ini mungkin tidak dapat diterima. Selain itu, Anda juga "melihat" (dalam IDE) kode aslinya, dan bukan kode yang dihasilkan; ini mungkin membuat proses debug menjadi sulit.

API Profiler

Dalam hal ini, Anda melakukan pekerjaan yang sama seperti untuk Reflection.Emit, tetapi Anda tidak perlu membuat "proxy" apa pun terlebih dahulu; kode disuntikkan pada waktu pembuatan IL (yaitu sekali, yang merupakan kinerja yang cukup baik). Anda cukup menulis DLL profiler (yang tidak dikelola), dan memulai program Anda di bawahnya. "Profiler" Anda akan diberi tahu tentang fungsi mana yang dijalankan (atau, lebih baik lagi, fungsi mana yang di-JIT) dan Anda dapat bertindak sesuai dengan itu.

Kerugian utama: Anda tidak dapat mengubah "struktur" (metode, bidang,...) kelas Anda (tidak mudah); Pembuatan IL dengan API yang tidak dikelola cukup sulit (Anda harus mengurus banyak hal, dan melakukan debug ketika terjadi kesalahan adalah mimpi buruk yang nyata!) Keuntungan utama: tidak diperlukan modifikasi pada kode asli (terutama, tidak diperlukan modifikasi pada sisi penelepon - inilah yang pada akhirnya mengarahkan saya menuju solusi ini, mengingat persyaratan proyek tertentu).

T4

Saya menggunakan T4 untuk proyek lain, tetapi saya dapat membagikan temuan saya di sini. Ini bagus untuk mengubah teks, tetapi tidak benar-benar untuk mengubah kode: sintaksis menjadi "aneh" (Anda memiliki kode, dan kode di dalam kode untuk menghasilkan kode... Anda mudah tersesat).

Keuntungan utama: terintegrasi dengan baik dengan IDE dan sistem build; Anda mendapatkan dukungan intelijen; mudah untuk memulainya; Anda benar-benar melihat apa yang sedang Anda debug

Secara pribadi, saya ingin memiliki (atau membangun sendiri) sesuatu seperti T4, tetapi itu melakukan transformasi kode C# -> C#; dengan cara itu, Anda bisa mendapatkan keuntungan dari IDE (termasuk intellisense pada barang "asli" dan "produksi") dan kekuatan Reflection.Emit. Anda mungkin perlu membuat kompiler C#/C#, dan plugin VS untuk mendapatkan semua manfaat ini.

Satu catatan terakhir: hari ini, saya akan menggunakan sesuatu yang lebih "canggih" daripada Reflection.Emit, seperti IKVM emit, atau Sigil

person Lorenzo Dematté    schedule 19.03.2013
comment
Tanggapan Anda menyeluruh. Terima kasih. Penelitian saya membawa saya ke solusi tambahan ini - PostSharp - yang akan menjadi solusi sempurna, jika fitur ini gratis (ini hanya sebagian dari fitur versi berbayar). Namun masih mempertimbangkan... - person Tar; 20.03.2013