Halo pembaca. Artikel ini menjelaskan atribut dari semua sisi - mulai dari spesifikasi, makna dan definisi atribut, membuat milik Anda dan bekerja dengannya, diakhiri dengan menambahkan atribut pada runtime dan atribut yang ada yang paling berguna dan menarik. Jika Anda tertarik pada topik atribut di C #, selamat datang di cat.
Isi
- Pendahuluan Menentukan dan Menetapkan Atribut
- Atribut yang menarik dengan dukungan runtime. Di sini, informasi singkat akan diberikan tentang berbagai atribut, keberadaan yang hanya sedikit orang ketahui dan bahkan kurang yang menggunakannya. Karena ini benar-benar informasi yang tidak praktis, tidak akan ada banyak omelan (bertentangan dengan hasrat saya untuk pengetahuan yang tidak dapat diterapkan)
- Beberapa atribut yang kurang dikenal yang bermanfaat untuk diketahui.
- Menentukan atribut Anda dan memprosesnya. Menambahkan Atribut saat Jalankan
Pendahuluan
Seperti biasa, mulailah dengan definisi dan spesifikasi. Ini akan membantu untuk memahami dan mewujudkan atribut di semua tingkatan, yang, pada gilirannya, sangat berguna untuk menemukan aplikasi yang tepat untuk mereka.
Mulailah dengan mendefinisikan metadata.
Metadata adalah data yang menjelaskan dan merujuk pada jenis yang didefinisikan oleh
CTS . Metadata disimpan dengan cara yang tidak tergantung pada bahasa pemrograman tertentu. Dengan demikian, metadata menyediakan mekanisme umum untuk bertukar informasi tentang suatu program untuk digunakan di antara alat-alat yang membutuhkannya (kompiler dan pengadu, serta program itu sendiri), serta antara
VES . Metadata termasuk dalam manifes perakitan. Mereka dapat disimpan dalam file
PE bersama dengan kode
IL atau dalam file PE terpisah, di mana hanya akan ada manifes perakitan.
Atribut adalah karakteristik dari suatu tipe atau anggotanya (atau konstruksi bahasa lainnya) yang berisi informasi deskriptif. Meskipun atribut yang paling umum sudah ditentukan sebelumnya dan memiliki format spesifik dalam metadata, atribut khusus juga dapat ditambahkan ke metadata. Atribut bersifat komutatif, mis. urutan deklarasi mereka atas elemen tidak penting
Dari sudut pandang sintaksis (dalam metadata), ada atribut berikut- Menggunakan sintaks khusus dalam IL. Misalnya, kata kunci adalah atribut. Dan bagi mereka ada sintaks khusus di IL. Ada cukup banyak dari mereka, daftar semuanya tidak masuk akal
- Menggunakan sintaks umum. Ini termasuk atribut pengguna dan perpustakaan.
- Atribut keamanan. Ini termasuk atribut yang mewarisi dari SecurityAttribute (langsung atau tidak langsung). Mereka diproses dengan cara khusus. Ada sintaks khusus untuk mereka di IL, yang memungkinkan Anda membuat xml yang menggambarkan atribut ini secara langsung
Contoh
Kode C # yang berisi semua jenis atribut di atas[StructLayout(LayoutKind.Explicit)] [Serializable] [Obsolete] [SecurityPermission(SecurityAction.Assert)] public class Sample { }
Menghasilkan IL .class public EXPLICIT ansi SERIALIZABLE beforefieldinit AttributeSamples.Sample extends [System.Runtime]System.Object { .custom instance void [System.Runtime]System.ObsoleteAttribute::.ctor() = (01 00 00 00 ) .permissionset assert = { class 'System.Security.Permissions.SecurityPermissionAttribute, System.Runtime.Extensions, Version=4.2.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' = {}} .method public hidebysig specialname rtspecialname instance void .ctor() cil managed {/*constructor body*/} }
Seperti yang Anda lihat, StructLayoutAttribute memiliki sintaks khusus, karena di IL direpresentasikan sebagai "eksplisit". ObsoleteAttribute menggunakan sintaksis umum - di IL dimulai dengan ".custom". SecurityPermissionAttribute sebagai atribut keamanan telah berubah menjadi ".permissionsetertert menegaskan".
Atribut pengguna menambahkan informasi pengguna ke metadata. Mekanisme ini dapat digunakan untuk menyimpan informasi spesifik aplikasi pada waktu kompilasi dan untuk mengaksesnya pada saat dijalankan atau untuk dibaca dan dianalisis oleh alat lain. Meskipun setiap tipe yang ditentukan pengguna dapat digunakan sebagai atribut, kesesuaian
CLS mensyaratkan bahwa atribut mewarisi dari System.Attribute.
CLI menentukan beberapa atribut dan menggunakannya untuk mengontrol perilaku runtime. Beberapa bahasa mendefinisikan atribut untuk mewakili fitur bahasa yang tidak diwakili secara langsung di CTS.
Seperti yang telah disebutkan, atribut disimpan dalam metadata, yang, pada gilirannya, dihasilkan pada tahap kompilasi, mis. dimasukkan dalam file PE (biasanya * .dll). Dengan demikian, Anda dapat menambahkan atribut saat runtime hanya dengan memodifikasi file yang dapat dieksekusi saat runtime (tetapi waktu program yang mengubah diri sudah lama hilang). Oleh karena itu mereka tidak dapat ditambahkan pada tahap eksekusi, tetapi ini tidak sepenuhnya akurat. Jika kita membentuk rakitan kita, tentukan tipe di dalamnya, maka kita dapat membuat tipe baru pada tahap eksekusi dan menggantung atribut di atasnya. Jadi secara formal, kita masih bisa menambahkan atribut saat runtime (contohnya ada di paling bawah).
Sekarang sedikit tentang batasannya
Jika karena alasan tertentu ada 2 atribut dalam rakitan yang sama dengan nama Name dan NameAtribute, maka menjadi tidak mungkin untuk menempatkan yang pertama dari mereka. Saat menggunakan [Nama] (yaitu, tanpa akhiran), kompiler mengatakan ia melihat ketidakpastian. Saat menggunakan [NameAttribute] kita akan meletakkan NameAttribute, yang logis. Ada sintaksis khusus untuk situasi mistis seperti itu dengan kurangnya imajinasi saat memberi nama. Untuk menempatkan versi pertama tanpa akhiran, Anda dapat menentukan tanda anjing (yaitu, [Nama] adalah lelucon, itu tidak perlu) sebelum nama atribut [@Nama].
Atribut khusus dapat ditambahkan ke apa pun selain atribut khusus. Ini mengacu pada metadata, mis. jika kita meletakkan atribut dalam C # di atas kelas atribut, maka dalam metadata itu akan merujuk ke kelas. Tetapi Anda tidak dapat menambahkan atribut ke "publik". Tetapi Anda dapat melakukannya dengan rakitan, modul, kelas, tipe nilai, enum, konstruktor, metode, properti, bidang, peristiwa, antarmuka, parameter, delegasi, nilai balik, atau parameter umum. Contoh di bawah ini menunjukkan contoh yang jelas dan tidak terlalu tentang bagaimana Anda dapat menempatkan atribut pada konstruksi tertentu.
Sintaksis Deklarasi Atribut using System; using System.Runtime.InteropServices; using System.Security.Permissions; using AttributeSamples; [assembly:All] [module:All] namespace AttributeSamples { [AttributeUsage(AttributeTargets.All)] public class AllAttribute : Attribute { } [All]
Atribut memiliki 2 jenis parameter - bernama dan posisional. Parameter posisi termasuk parameter konstruktor. Untuk menamai - properti publik dengan setter yang dapat diakses. Selain itu, ini bukan hanya nama formal, semua parameter dapat ditunjukkan saat mendeklarasikan atribut dalam tanda kurung setelah namanya. Yang disebutkan adalah opsional.
Jenis-jenis Parameter [AttributeUsage(AttributeTargets.All, AllowMultiple = true)] public class AttrParamsAttribute : Attribute { public AttrParamsAttribute(int positional)
Parameter yang valid (dari kedua jenis) untuk atribut harus salah satu dari jenis berikut:
- bool, byte, char, double, float, int, panjang, pendek, string dan selanjutnya pada primitif, kecuali desimal
- objek
- Jenis Sistem
- enum
- Array satu dimensi dari salah satu tipe di atas
Ini sebagian besar disebabkan oleh fakta bahwa ia harus berupa konstanta waktu kompilasi, dan tipe-tipe di atas dapat menerima konstanta ini (dengan menerima objek yang dapat kita lewati int). Tetapi untuk beberapa alasan, argumen tidak bisa dari tipe ValueType, meskipun ini mungkin dari sudut pandang logis.
Ada dua jenis atribut pengguna: atribut custom asli dan
pseudo-custom .
Dalam kode, mereka terlihat sama (ditunjukkan di atas struktur bahasa dalam tanda kurung), tetapi mereka diproses secara berbeda:
- Atribut pengguna asli disimpan langsung di metadata; parameter atribut disimpan apa adanya. Mereka tersedia saat runtime dan disimpan sebagai satu set byte (saya harus mengingatkan Anda bahwa mereka dikenal pada waktu kompilasi)
- Atribut pseudo-pengguna dikenali karena namanya adalah salah satu dari daftar khusus. Alih-alih menyimpan datanya secara langsung dalam metadata, itu dianalisis dan digunakan untuk mengatur bit atau bidang dalam tabel metadata, dan data tersebut kemudian dibuang dan tidak dapat diterima lebih lanjut. Tabel metadata diperiksa pada waktu runtime lebih cepat daripada atribut pengguna asli, dan penyimpanan yang lebih sedikit diperlukan untuk menyimpan informasi.
Atribut pseudo-pengguna tidak refleksi terlihat [Serializable] [StructLayout(LayoutKind.Explicit)] public class CustomPseudoCustom { } class Program { static void Main() { var onlyCustom = typeof(CustomPseudoCustom).GetCustomAttributes();
Sebagian besar atribut pengguna diperkenalkan di tingkat bahasa. Mereka disimpan dan dikembalikan oleh runtime, sedangkan runtime tidak tahu apa-apa tentang arti dari atribut ini. Tetapi semua atribut pseudo-pengguna ditambah beberapa atribut pengguna sangat menarik bagi penyusun dan CLI. Jadi kita beralih ke bagian selanjutnya.
Atribut yang diaktifkan runtime
Bagian ini murni informatif, jika tidak ada minat menggunakan runtime, maka Anda dapat menggulir ke bagian berikutnya.
Tabel di bawah ini mencantumkan atribut pseudo-pengguna dan atribut pengguna khusus (CLI atau kompiler menanganinya dengan cara khusus).
Atribut pseudo-pengguna (mereka tidak dapat diperoleh melalui refleksi).
Atribut CLI:
Atribut CLS - Bahasa harus mendukungnya:
Lain-lain yang menarik
Atribut yang Berguna
Bagian integral dari pengembangan produk perangkat lunak adalah debugging. Dan seringkali dalam sistem yang besar dan kompleks dibutuhkan puluhan dan ratusan kali untuk menjalankan metode yang sama dan memantau keadaan objek. Pada saat yang sama, pada waktu 20 sudah mulai membuat marah kebutuhan khusus untuk memperluas satu objek dalam 400 kali untuk melihat nilai satu variabel dan me-restart metode lagi.
Untuk debugging yang lebih tenang dan lebih cepat, Anda bisa menggunakan atribut yang mengubah perilaku debugger.
DebuggerDisplayAttribute menunjukkan bagaimana tipe atau anggotanya ditampilkan di jendela variabel debugger (dan tidak hanya).
Satu-satunya argumen untuk konstruktor adalah string dengan format tampilan. Apa yang akan terjadi antara kawat gigi akan dihitung. Formatnya seperti string interpolasi, hanya tanpa dolar. Anda tidak dapat menggunakan pointer dalam nilai yang dihitung. Omong-omong, jika Anda memiliki ToString yang diganti, maka nilainya akan ditampilkan seolah-olah berada di atribut ini. Jika ada ToString dan atribut, maka nilainya diambil dari atribut tersebut.
DebuggerBrowsableAttribute mendefinisikan bagaimana bidang atau properti ditampilkan di jendela variabel debugger. Menerima DebuggerBrowsableState, yang memiliki 3 opsi:
- Tidak pernah - bidang tidak ditampilkan sama sekali selama debugging. Saat memperluas hierarki objek, bidang ini tidak akan ditampilkan
- Runtuh - bidang tidak terpecahkan, tetapi dapat diperluas. Ini adalah perilaku default.
- RootHidden - bidang itu sendiri tidak ditampilkan, tetapi objek yang terdiri ditampilkan (untuk array dan koleksi)
DebuggerTypeProxy - jika objek dilihat dalam debugger ratusan kali sehari, Anda bisa bingung dan menghabiskan 3 menit membuat objek proxy yang menampilkan objek asli sebagaimana mestinya. Biasanya, objek proxy untuk ditampilkan adalah kelas dalam. Sebenarnya, itu akan ditampilkan bukan objek target.

Atribut berguna lainnya
ThreadStatic - atribut yang memungkinkan Anda membuat variabel statis sendiri untuk setiap utas. Untuk melakukan ini, letakkan atribut di atas bidang statis. Perlu diingat nuansa penting - inisialisasi oleh konstruktor statis akan dilakukan hanya sekali, dan variabel akan berubah di utas yang akan dijalankan oleh konstruktor statis. Selebihnya, itu akan tetap default. (PS. Jika Anda memerlukan perilaku ini, saya menyarankan Anda untuk melihat ke arah kelas ThreadLocal).
Sedikit tentang nuansa kompartemen mesin. Baik di Linux dan Windows, ada area memori lokal untuk streaming (
TLS dan
TSD, masing-masing). Namun, area ini sendiri sangat kecil. Oleh karena itu, struktur ThreadLocalInfo dibuat, sebuah penunjuk yang ditempatkan di TLS. Dengan demikian, hanya satu slot yang digunakan. Struktur itu sendiri berisi 3 bidang - Utas, AppDomain, ClrTlsInfo. Kami tertarik pada yang pertama. Dialah yang mengatur penyimpanan statika aliran dalam memori, menggunakan ThreadLocalBlock dan ThreadLocalModule untuk ini.
Dengan cara ini:
- Jenis referensi - terletak di heap, ThreadStaticHandleTable, yang didukung oleh kelas ThreadLocalBlock, menyimpan tautan ke sana.
- Struktur - Dikemas dan disimpan dalam tumpukan yang dikelola serta jenis referensi
- Tipe signifikan primitif disimpan di area memori yang tidak dikelola yang merupakan bagian dari ThreadLocalModule
Nah, karena kita berbicara tentang ini, perlu disebutkan tentang metode asinkron. Seperti yang mungkin diperhatikan oleh pembaca yang perhatian, jika kita menggunakan asinkron, maka kelanjutan tidak harus dieksekusi di utas yang sama (kita dapat memengaruhi konteks eksekusi, tetapi bukan utas). Karenanya, kami mendapatkan omong kosong jika kami menggunakan ThreadLocal. Dalam hal ini, disarankan untuk menggunakan AsyncLocal. Tapi artikelnya bukan tentang ini, jadi kami melangkah lebih jauh.
InternalsVisibleTo - memungkinkan Anda menentukan rakitan, yang akan terlihat oleh elemen yang ditandai
internal . Tampaknya jika beberapa majelis membutuhkan jenis tertentu dan anggotanya, Anda dapat dengan mudah menandai mereka di depan
umum dan bukan uap. Tetapi arsitektur yang baik menyiratkan menyembunyikan detail implementasi. Namun demikian, mereka mungkin diperlukan untuk beberapa hal infrastruktur, misalnya, proyek uji. Dengan menggunakan atribut ini, Anda dapat mendukung enkapsulasi dan persentase cakupan pengujian yang diperlukan.
HandleProcessCorruptedStateExceptions - memungkinkan Anda untuk menakut-nakuti programmer yang pemalu dan menangkap pengecualian dari kondisi yang rusak. Secara default, untuk pengecualian seperti itu, CLR tidak menjebak. Secara umum, solusi terbaik adalah membiarkan aplikasi crash. Ini adalah pengecualian berbahaya yang menunjukkan bahwa memori proses rusak, jadi menggunakan atribut ini adalah ide yang sangat buruk. Tetapi mungkin dalam beberapa kasus, untuk pengembangan lokal akan berguna untuk mengatur atribut ini untuk sementara waktu. Untuk mengetahui pengecualian kondisi rusak, cukup letakkan atribut ini di atas metode. Dan jika telah mencapai penggunaan atribut ini, disarankan (namun, seperti biasa) untuk menangkap beberapa pengecualian khusus.
DisablePrivateReflection - membuat semua anggota pribadi majelis tidak dapat dijangkau untuk refleksi. Atribut diletakkan di majelis.
Menentukan Atribut Anda
Bukan hanya karena bagian ini adalah yang terakhir. Lagi pula, cara terbaik untuk memahami dalam kasus apa akan bermanfaat untuk menggunakan atribut adalah dengan melihat yang sudah digunakan. Sulit untuk mengatakan aturan formal ketika Anda harus memikirkan atribut Anda sendiri. Seringkali mereka digunakan sebagai informasi tambahan tentang jenis / anggotanya atau konstruksi bahasa lain yang umum untuk entitas yang sama sekali berbeda. Sebagai contoh, semua atribut yang digunakan untuk serialisasi / ORM / pemformatan, dll. Karena aplikasi yang luas dari mekanisme ini untuk jenis yang sama sekali berbeda, sering tidak diketahui oleh pengembang mekanisme yang sesuai, penggunaan atribut adalah cara yang bagus untuk memungkinkan pengguna untuk memberikan informasi deklaratif untuk mekanisme ini.
Menggunakan atribut Anda dapat dibagi menjadi 2 bagian:
- Membuat atribut dan menggunakannya
- Mendapatkan atribut dan memprosesnya
Membuat atribut dan menggunakannya
Untuk membuat atribut Anda, cukup mewarisi dari
System.Attribute . Dalam hal ini, disarankan untuk mematuhi gaya penamaan yang disebutkan - akhiri nama kelas pada Atribut. Namun, tidak akan ada kesalahan jika Anda menghilangkan sufiks ini. Seperti disebutkan sebelumnya, atribut dapat memiliki 2 jenis parameter - posisi dan nama. Logika aplikasi mereka sama dengan properti dan parameter konstruktor kelas - nilai yang diperlukan untuk membuat objek yang tidak ada "default" yang masuk akal ditempatkan dalam posisi (yaitu, konstruktor). Apa yang dapat secara wajar default, yang akan sering digunakan, lebih baik dibedakan menjadi satu bernama (yaitu properti).
Yang tidak kalah pentingnya dalam menciptakan atribut adalah keterbatasan tempat aplikasinya. AttributeUsageAttribute digunakan untuk ini. Parameter yang diperlukan (posisional) adalah AttributeTarget, yang menentukan di mana atribut digunakan (metode, perakitan, dll.). Parameter opsional (bernama) adalah:
- AllowMultiple - menunjukkan apakah mungkin untuk menempatkan lebih dari satu atribut di atas tempat penerapannya atau tidak. Salah secara default
- Mewarisi - menentukan apakah atribut ini akan menjadi milik pewaris kelas (dalam hal penempatan di atas kelas dasar) dan metode yang diganti (dalam kasus penempatan di atas metode). Defaultnya benar.
Setelah itu, Anda dapat memuat atribut dengan payload. Atribut adalah informasi deklaratif, yang berarti segala sesuatu yang didefinisikan di dalamnya harus menggambarkan konstruksi yang terkait. Atribut tidak boleh mengandung logika yang mendalam. Untuk pemrosesan atribut yang Anda tentukan, layanan khusus harus bertanggung jawab yang hanya akan memprosesnya. Tetapi fakta bahwa atribut tidak boleh memiliki logika tidak berarti bahwa itu tidak boleh memiliki metode.
Suatu metode (fungsi) juga merupakan informasi dan juga dapat menggambarkan suatu desain. Dan menggunakan polimorfisme dalam atribut, Anda dapat memberikan alat yang sangat kuat dan nyaman di mana pengguna dapat memengaruhi informasi yang digunakan oleh alat Anda dan tahapan eksekusi dan pemrosesan tertentu.
Dalam hal ini, ia tidak perlu membuat kelas, menyuntikkan dependensi, biaya pabrik dan antarmuka mereka yang akan membuat kelas ini. Itu akan cukup untuk membuat kelas pewaris tunggal yang merangkum detail bekerja dengan elemen yang terkait. Tetapi, sebagai aturan, atribut ROSO biasa dengan beberapa properti sudah cukup.Mengambil dan memproses atribut
Pemrosesan atribut yang diterima tergantung pada kasus spesifik dan dapat dilakukan dengan cara yang sangat berbeda. Sulit untuk memberikan fungsi dan trik yang bermanfaat untuk ini.Atribut diperoleh pada saat run time menggunakan refleksi. Ada berbagai cara untuk mendapatkan atribut dari elemen tertentu.Tapi semuanya berasal dari antarmuka ICustomAttributeProvider . Ini diimplementasikan oleh tipe seperti Assembly, MemberInfo, Module, ParameterInfo. Pada gilirannya, penerus MemberInfo adalah Jenis, EventInfo, FieldInfo, MethodBase, PropertyInfo.Antarmuka hanya memiliki 3 fungsi, dan mereka tidak terlalu nyaman. Mereka bekerja dengan array (bahkan jika kita tahu bahwa hanya ada satu atribut) dan tidak parameter berdasarkan tipe (mereka menggunakan objek). Karena itu, Anda jarang harus secara langsung mengakses fungsi antarmuka ini (saya tidak pernah mengatakannya karena saya tidak ingin menjadi kategori). Untuk kemudahan penggunaan, ada kelas CustomAttributeExtensions , di mana ada banyak metode ekstensi untuk semua jenis jenis yang melakukan operasi sederhana untuk casting, memilih nilai tunggal, dan sebagainya, sehingga membebaskan pengembang dari kebutuhan ini. Selain itu, metode ini tersedia sebagai statis di kelas Atribut dengan fungsi yang paling berguna untuk mengabaikan parameter inherit (untuk non-konformis).Fungsi utama yang digunakan tercantum di bawah ini. Parameter pertama yang menunjukkan tipe mana yang memperluas metode, saya hilangkan. Juga, di mana pun parameter pewarisan bool ditentukan, ada kelebihan tanpa itu (dengan nilai default true ). Parameter ini menentukan apakah atribut dari kelas induk atau metode dasar harus dipertimbangkan ketika mengeksekusi metode (jika digunakan pada metode yang diganti). Jika dalam atribut inherit = flase , maka bahkan menyetelnya ke true tidak akan membantu memperhitungkan atribut dari kelas dasarUntuk kejelasan, saya mengusulkan untuk melihat demo kecil dari semua fungsi yang disebutkan.Nilai pengembalian metode di atas [AttributeUsage(AttributeTargets.All, AllowMultiple = true)] public class LogAttribute : Attribute { public string LogName { get; set; } } [AttributeUsage(AttributeTargets.All, AllowMultiple = true, Inherited = false)] public class SerializeAttribute : Attribute { public string SerializeName { get; set; } } [Log(LogName = "LogBase1")] [Log(LogName = "LogBase2")] [Serialize(SerializeName = "SerializeBase1")] [Serialize(SerializeName = "SerializeBase2")] public class RandomDomainEntityBase { [Log(LogName = "LogMethod1")] [Log(LogName = "LogMethod2")] [Serialize(SerializeName = "SerializeMethod1")] [Serialize(SerializeName = "SerializeMethod2")] public virtual void VirtualMethod() { throw new NotImplementedException(); } } [Log(LogName = "LogDerived1")] [Log(LogName = "LogDerived2")] [Serialize(SerializeName = "SerializeDerived1")] [Serialize(SerializeName = "SerializeDerived2")] public class RandomDomainEntityDerived : RandomDomainEntityBase { [Log(LogName = "LogOverride1")] [Log(LogName = "LogOverride2")] [Serialize(SerializeName = "SerializeOverride1")] [Serialize(SerializeName = "SerializeOverride2")] public override void VirtualMethod() { throw new NotImplementedException(); } } class Program { static void Main() { Type derivedType = typeof(RandomDomainEntityDerived); MemberInfo overrideMethod = derivedType.GetMethod("VirtualMethod");
Nah, untuk kepentingan akademis, saya memberikan contoh mendefinisikan atribut saat runtime. Kode ini tidak mengklaim sebagai yang paling indah dan didukung.Kode public class TypeCreator { private const string TypeSignature = "DynamicType"; private const string ModuleName = "DynamicModule"; private const string AssemblyName = "DynamicModule"; private readonly TypeBuilder _typeBuilder = GetTypeBuilder(); public object CreateTypeInstance() { _typeBuilder.DefineNestedType("ClassName"); CreatePropertyWithAttribute<SerializeAttribute>("PropWithAttr", typeof(int), new Type[0], new object[0]); Type newType = _typeBuilder.CreateType(); return Activator.CreateInstance(newType); } private void CreatePropertyWithAttribute<T>(string propertyName, Type propertyType, Type[] ctorTypes, object[] ctorArgs) where T : Attribute { var attributeCtor = typeof(T).GetConstructor(ctorTypes); CustomAttributeBuilder caBuilder = new CustomAttributeBuilder(attributeCtor, ctorArgs); PropertyBuilder newProperty = CreateProperty(propertyName, propertyType); newProperty.SetCustomAttribute(caBuilder); } private PropertyBuilder CreateProperty(string propertyName, Type propertyType) { FieldBuilder fieldBuilder = _typeBuilder.DefineField(propertyName, propertyType, FieldAttributes.Private); PropertyBuilder propertyBuilder = _typeBuilder.DefineProperty(propertyName, PropertyAttributes.HasDefault, propertyType, null); MethodAttributes getSetAttr = MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig; MethodBuilder getter = GenerateGetter(); MethodBuilder setter = GenerateSetter(); propertyBuilder.SetGetMethod(getter); propertyBuilder.SetSetMethod(setter); return propertyBuilder; MethodBuilder GenerateGetter() { MethodBuilder getMethodBuilder = _typeBuilder.DefineMethod($"get_{propertyName}", getSetAttr, propertyType, Type.EmptyTypes); ILGenerator getterIl = getMethodBuilder.GetILGenerator(); getterIl.Emit(OpCodes.Ldarg_0); getterIl.Emit(OpCodes.Ldfld, fieldBuilder); getterIl.Emit(OpCodes.Ret); return getMethodBuilder; } MethodBuilder GenerateSetter() { MethodBuilder setMethodBuilder = _typeBuilder.DefineMethod($"set_{propertyName}", getSetAttr, null,new [] { propertyType }); ILGenerator setterIl = setMethodBuilder.GetILGenerator(); setterIl.Emit(OpCodes.Ldarg_0); setterIl.Emit(OpCodes.Ldarg_1); setterIl.Emit(OpCodes.Stfld, fieldBuilder); setterIl.Emit(OpCodes.Ret); return setMethodBuilder; } } private static TypeBuilder GetTypeBuilder() { var assemblyBuilder = AssemblyBuilder.DefineDynamicAssembly(new AssemblyName(AssemblyName), AssemblyBuilderAccess.Run); ModuleBuilder moduleBuilder = assemblyBuilder.DefineDynamicModule(ModuleName); TypeBuilder typeBuilder = moduleBuilder.DefineType(TypeSignature, TypeAttributes.Public | TypeAttributes.Class | TypeAttributes.AutoClass | TypeAttributes.AnsiClass | TypeAttributes.BeforeFieldInit | TypeAttributes.AutoLayout,null); return typeBuilder; } } public class Program { public static void Main() { object instance = new TypeCreator().CreateTypeInstance(); IEnumerable<Attribute> attrs = instance.GetType().GetProperty("PropWithAttr").GetCustomAttributes();