Untuk kelas tertentu saya ingin memiliki fungsi penelusuran yaitu saya ingin mencatat setiap pemanggilan metode (metode tanda tangan dan nilai parameter aktual) dan setiap metode keluar (hanya metode tanda tangan).
Bagaimana saya melakukan ini dengan asumsi bahwa:
- Saya tidak ingin menggunakan perpustakaan AOP pihak ketiga mana pun untuk C #,
- Saya tidak ingin menambahkan kode duplikat ke semua metode yang ingin saya lacak,
- Saya tidak ingin mengubah API publik dari kelas - pengguna kelas harus dapat memanggil semua metode dengan cara yang persis sama.
Untuk membuat pertanyaan lebih konkret mari kita asumsikan ada 3 kelas:
public class Caller
{
public static void Call()
{
Traced traced = new Traced();
traced.Method1();
traced.Method2();
}
}
public class Traced
{
public void Method1(String name, Int32 value) { }
public void Method2(Object object) { }
}
public class Logger
{
public static void LogStart(MethodInfo method, Object[] parameterValues);
public static void LogEnd(MethodInfo method);
}
Bagaimana cara memanggil Logger.LogStart dan Logger.LogEnd untuk setiap panggilan ke Method1 dan Method2 tanpa memodifikasi metode Caller.Call dan tanpa menambahkan panggilan secara eksplisit ke Traced.Method1 dan Traced.Method2 ?
Sunting: Apa yang akan menjadi solusi jika saya diizinkan sedikit mengubah metode Panggilan?
c#
reflection
aop
Pekerja harian
sumber
sumber
Jawaban:
C # bukan bahasa yang berorientasi AOP. Ini memiliki beberapa fitur AOP dan Anda dapat meniru beberapa yang lain tetapi membuat AOP dengan C # itu menyakitkan.
Saya mencari cara untuk melakukan persis apa yang ingin Anda lakukan dan saya tidak menemukan cara mudah untuk melakukannya.
Seperti yang saya pahami, inilah yang ingin Anda lakukan:
dan untuk melakukan itu Anda memiliki dua opsi utama
Mewarisi kelas Anda dari MarshalByRefObject atau ContextBoundObject dan tentukan atribut yang diwarisi dari IMessageSink. Artikel ini memiliki contoh yang bagus. Anda harus mempertimbangkan nontheless bahwa menggunakan MarshalByRefObject kinerja akan turun seperti neraka, dan maksud saya, saya sedang berbicara tentang kinerja 10x hilang jadi pikirkan baik-baik sebelum mencoba itu.
Pilihan lainnya adalah menyuntikkan kode secara langsung. Dalam runtime, artinya Anda harus menggunakan refleksi untuk "membaca" setiap kelas, mendapatkan atributnya dan menyuntikkan panggilan yang sesuai (dan dalam hal ini saya pikir Anda tidak dapat menggunakan metode Reflection. Kirim seperti yang saya pikir Reflection.Emit wouldn memungkinkan Anda memasukkan kode baru ke dalam metode yang sudah ada). Pada waktu desain ini berarti membuat ekstensi ke kompiler CLR yang saya jujur tidak tahu bagaimana melakukannya.
Opsi terakhir menggunakan kerangka kerja IoC . Mungkin itu bukan solusi yang sempurna karena sebagian besar kerangka kerja IoC bekerja dengan mendefinisikan titik masuk yang memungkinkan metode untuk dihubungkan tetapi, tergantung pada apa yang ingin Anda capai, itu mungkin merupakan perkiraan yang adil.
sumber
Reflection.Emit
. Ini adalah pendekatan yang dipilih oleh Spring.NET . Namun, ini akan memerlukan metode virtualTraced
dan tidak benar-benar cocok untuk digunakan tanpa semacam wadah IOC, jadi saya mengerti mengapa opsi ini tidak ada dalam daftar Anda.Cara paling sederhana untuk mencapai itu mungkin menggunakan PostSharp . Itu menyuntikkan kode di dalam metode Anda berdasarkan atribut yang Anda terapkan padanya. Ini memungkinkan Anda untuk melakukan apa yang Anda inginkan.
Pilihan lain adalah menggunakan API profil untuk menyuntikkan kode di dalam metode, tetapi itu benar-benar hardcore.
sumber
Jika Anda menulis sebuah kelas - sebut saja Tracing - yang mengimplementasikan antarmuka IDisposable, Anda bisa membungkus semua badan metode dalam sebuah
Di kelas Tracing Anda bisa menangani logika jejak di konstruktor / Buang metode, masing-masing, di kelas Tracing untuk melacak masuk dan keluarnya metode. Seperti yang:
sumber
Anda bisa mencapainya dengan fitur Intersepsi wadah DI seperti Castle Windsor . Memang, adalah mungkin untuk mengkonfigurasi wadah sedemikian rupa sehingga setiap kelas yang memiliki metode yang didekorasi oleh atribut tertentu akan dicegat.
Mengenai poin # 3, OP meminta solusi tanpa kerangka kerja AOP. Saya berasumsi dalam jawaban berikut bahwa apa yang harus dihindari adalah Aspect, JointPoint, PointCut, dll. Menurut dokumentasi Interception dari CastleWindsor , tidak ada yang diminta untuk memenuhi apa yang diminta.
Konfigurasikan pendaftaran generik Interceptor, berdasarkan keberadaan atribut:
Tambahkan IContributeComponentModelConstruction yang dibuat ke wadah
Dan Anda dapat melakukan apa pun yang Anda inginkan di interceptor itu sendiri
Tambahkan atribut logging ke metode Anda untuk login
Perhatikan bahwa beberapa penanganan atribut akan diperlukan jika hanya beberapa metode kelas yang perlu dicegat. Secara default, semua metode publik akan dicegat.
sumber
Jika Anda ingin melacak setelah metode Anda tanpa batasan (tidak ada adaptasi kode, tidak ada Kerangka AOP, tidak ada kode duplikat), izinkan saya memberi tahu Anda, Anda memerlukan beberapa keajaiban ...
Serius, saya memutuskan untuk mengimplementasikan Kerangka Kerja AOP yang bekerja saat runtime.
Anda dapat menemukan di sini: NConcern .NET AOP Framework
Saya memutuskan untuk membuat Kerangka AOP ini untuk merespons kebutuhan semacam ini. itu adalah perpustakaan sederhana yang sangat ringan. Anda dapat melihat contoh logger di beranda.
Jika Anda tidak ingin menggunakan rakitan pihak ke-3, Anda dapat menelusuri sumber kode (sumber terbuka) dan menyalin kedua file Aspect.Directory.cs dan Aspect.Directory.Entry.cs untuk diadaptasi sesuai keinginan Anda. Kelas-kelas tesis memungkinkan untuk mengganti metode Anda saat runtime. Saya hanya akan meminta Anda untuk menghormati lisensi.
Saya harap Anda akan menemukan apa yang Anda butuhkan atau meyakinkan Anda untuk akhirnya menggunakan Kerangka Kerja AOP.
sumber
Lihatlah ini - Barang yang cukup berat .. http://msdn.microsoft.com/en-us/magazine/cc164165.aspx
Essential .net - don box memiliki bab tentang apa yang Anda butuhkan disebut Interception. Saya menggaruknya di sini (Maaf tentang warna font - saya punya tema gelap waktu itu ...) http://madcoderspeak.blogspot.com/2005/09/essential-interception-using-contexts.html
sumber
Saya telah menemukan cara berbeda yang mungkin lebih mudah ...
Deklarasikan Metode InvokeMethod
Saya kemudian mendefinisikan metode saya seperti itu
Sekarang saya dapat memiliki cek pada saat dijalankan tanpa injeksi ketergantungan ...
Tidak ada gotcha di situs :)
Semoga Anda akan setuju bahwa ini lebih ringan daripada Kerangka Kerja AOP atau berasal dari MarshalByRefObject atau menggunakan kelas remoting atau proxy.
sumber
Pertama, Anda harus memodifikasi kelas Anda untuk mengimplementasikan antarmuka (daripada mengimplementasikan MarshalByRefObject).
Selanjutnya Anda memerlukan objek pembungkus generik berdasarkan RealProxy untuk menghias antarmuka apa pun untuk memungkinkan mencegat panggilan apa pun ke objek yang didekorasi.
Sekarang kita siap untuk mencegat panggilan ke Method1 dan Method2 dari ITraced
sumber
Anda dapat menggunakan framework open source CInject pada CodePlex. Anda dapat menulis kode minimal untuk membuat Injector dan mendapatkannya untuk mencegat kode apa pun dengan cepat dengan CInject. Plus, karena ini adalah Open Source, Anda dapat memperpanjang ini juga.
Atau Anda dapat mengikuti langkah-langkah yang disebutkan pada artikel ini tentang Mencegat Metode Panggilan menggunakan IL dan membuat pencegat Anda sendiri menggunakan kelas Reflection.Emit di C #.
sumber
Saya tidak tahu solusinya tetapi pendekatan saya adalah sebagai berikut.
Hiasi kelas (atau metodenya) dengan atribut khusus. Di tempat lain dalam program, biarkan fungsi inisialisasi mencerminkan semua jenis, baca metode yang didekorasi dengan atribut dan masukkan beberapa kode IL ke dalam metode. Mungkin sebenarnya lebih praktis untuk mengganti metode dengan sebuah rintisan yang memanggil
LogStart
, metode aktual dan kemudianLogEnd
. Selain itu, saya tidak tahu apakah Anda dapat mengubah metode menggunakan refleksi sehingga mungkin lebih praktis untuk mengganti seluruh tipe.sumber
Anda dapat berpotensi menggunakan Pola Dekorator GOF, dan 'mendekorasi' semua kelas yang perlu ditelusuri.
Mungkin hanya benar-benar praktis dengan wadah IOC (tetapi sebagai penunjuk keluar sebelumnya Anda mungkin ingin mempertimbangkan metode intersepsi jika Anda akan pergi ke jalur IOC).
sumber
Anda perlu bug Ayende untuk jawaban tentang bagaimana dia melakukannya: http://ayende.com/Blog/archive/2009/11/19/can-you-hack-this-out.aspx
sumber
AOP adalah suatu keharusan untuk menerapkan kode bersih, namun jika Anda ingin mengelilingi blok dalam C #, metode umum memiliki penggunaan yang relatif lebih mudah. (dengan akal dan kode yang sangat diketik) Tentu saja, itu TIDAK bisa menjadi alternatif untuk AOP.
Meskipun PostSHarp memiliki sedikit masalah kereta (saya tidak merasa percaya diri untuk digunakan di produksi), itu adalah hal yang baik.
Kelas pembungkus umum,
penggunaan bisa seperti ini (tentu saja dengan rasa intelli)
sumber
sumber
Yang terbaik yang dapat Anda lakukan sebelum C # 6 dengan 'nameof' dirilis adalah menggunakan StackTrace dan Linq Expressions lambat.
Misalnya untuk metode semacam itu
Baris seperti itu dapat diproduksi di file log Anda
Berikut implementasinya:
sumber