Bisakah atribut ditambahkan secara dinamis dalam C #?

143

Apakah mungkin menambahkan atribut saat runtime atau mengubah nilai atribut saat runtime?

Jon Turner
sumber

Jawaban:

67

Atribut adalah metadata statis. Rakitan, modul, tipe, anggota, parameter, dan nilai pengembalian bukan objek kelas satu di C # (misalnya, System.Typekelas hanyalah representasi yang tercermin dari suatu jenis). Anda bisa mendapatkan instance atribut untuk tipe dan mengubah properti jika itu dapat ditulisi tetapi itu tidak akan mempengaruhi atribut seperti yang diterapkan pada tipe.

Mark Cidade
sumber
68

Ini sangat tergantung pada apa yang sebenarnya ingin Anda capai.

Hal- hal System.ComponentModel.TypeDescriptor dapat digunakan untuk menambahkan atribut ke tipe, properti, dan instance objek, dan memiliki batasan bahwa Anda harus menggunakannya untuk mengambil properti tersebut juga. Jika Anda menulis kode yang menggunakan atribut tersebut, dan Anda dapat hidup dalam batasan itu, maka saya pasti akan menyarankannya.

Sejauh yang saya tahu, kontrol PropertyGrid dan permukaan desain studio visual adalah satu-satunya hal di BCL yang mengkonsumsi barang-barang TypeDescriptor. Bahkan, itulah cara mereka melakukan setengah dari hal-hal yang benar-benar perlu mereka lakukan.

Alex Lyman
sumber
7
Sebenarnya, sebagian besar penggunaan data-binding TypeDescriptor- bukan hanya PropertyGrid.
Marc Gravell
1
Solusi apa pun untuk menambahkan atribut metadata properti dalam proyek Silverlight (di mana TypeDescriptordan TypeDescriptionProvidertidak diimplementasikan?
Shimmy Weitzhandler
1
Penting untuk dicatat, TypeDescriptor.GetAttributes () tidak menangani atribut duplikat. Ini hanya memilih yang terakhir dari tipe atribut. Mantan [Attr(1), Attr(2), Attr(3)]hanya Attr(3)ditemukan.
ohmusama
11

Kamu tidak bisa Salah satu solusi mungkin untuk menghasilkan kelas turunan saat runtime dan menambahkan atribut, meskipun ini mungkin sedikit berlebihan.

petr k.
sumber
10

Nah, untuk menjadi berbeda, saya menemukan artikel yang referensi menggunakan Reflection.Emit untuk melakukannya.

Berikut tautannya: http://www.codeproject.com/KB/cs/dotnetattributes.aspx , Anda juga ingin melihat beberapa komentar di bagian bawah artikel, karena pendekatan yang mungkin dibahas.

torial
sumber
10
Perhatikan bahwa Anda dapat membuat atribut saat runtime dengan kelas Reflection.Emit, TETAPI Anda dapat mengikatnya ke kelas yang telah Anda bangun dengan paket Emit dan bukan ke yang sudah ada.
Panos
jawaban yang tidak berguna =)) kita semua peduli tentang kelas yang ada di sini, bukan yang dinamis.
Putus asa
@Hopeless, Anda bisa YourClassmenjadi subkelas YourRuntimeClassWithAttributes.
Motes
@Motes tidak yakin apa yang Anda maksudkan, kelas saya sudah ditentukan sebelumnya, itu berarti semua kelas dasar (yang mewarisi kelas saya) juga harus didefinisikan / ditentukan sebelumnya. Saya tidak bisa memikirkan cara apa pun untuk terlibat dengan sesuatu yang secara dinamis dibuat menggunakan Reflection.Emit.
Putus asa
1
@Hopeless, jika Anda ingin menambahkan atribut secara dinamis ke kelas yang sudah ada YourClass, Anda dapat mensubklasifikasikannya pada saat runtime dan menghasilkan kelas yang identik dengan nama yang sedikit berbeda yang juga memiliki atribut yang dibuat secara dinamis yang diinginkan, dan polimorfisme akan memungkinkan jenis kode pengecekan untuk tetap mengidentifikasi baseclass Anda.
Motes
4

Tidak, tidak.

Atribut adalah meta-data dan disimpan dalam bentuk biner di rakitan yang dikompilasi (itu juga sebabnya Anda hanya dapat menggunakan tipe sederhana di dalamnya).

Thomas Danecker
sumber
3

Saya tidak percaya begitu. Bahkan jika saya salah, hal terbaik yang dapat Anda harapkan adalah menambahkannya ke seluruh Tipe, tidak pernah menjadi instance dari Tipe.

Joel Coehoorn
sumber
22
TypeDescriptor.AddAttributes (Object, Attribute []) menambahkan atribut level kelas ke instance komponen target.
Peter Wone
3

Jika Anda membutuhkan sesuatu untuk dapat ditambahkan secara dinamis, atribut c # bukanlah caranya. Lihatlah ke dalam menyimpan data dalam xml. Saya baru-baru ini melakukan proyek yang saya mulai w / atribut, tetapi akhirnya pindah ke serialisasi w / xml.

Darren Kopp
sumber
1
mungkin itu bukan cara yang indah tetapi cara banyak perpustakaan lain memilih untuk menggunakan, dan untuk menyesuaikan perilaku perpustakaan itu, kami memiliki persyaratan untuk bermain dengan refleksi =)) benar-benar jalan buntu.
Putus asa
3

Mengapa Anda harus melakukannya? Atribut memberikan informasi tambahan untuk refleksi, tetapi jika Anda secara eksternal tahu properti mana yang Anda inginkan, Anda tidak membutuhkannya.

Anda bisa menyimpan data meta secara eksternal relatif mudah dalam database atau file sumber daya.

Keith
sumber
1
Penghapusan boilerplate. Bukankah akan berguna jika Anda dapat memiliki kelas secara otomatis menghasilkan atribut berdasarkan kode di dalam kelas? Saya mencoba mencari tahu sesuatu seperti ini untuk mengurangi boilerplate di objek SQL CLR. Akan mudah dalam bahasa lain ... lihat paulgraham.com/avg.html
Duncan Bayne
1

Saya berusaha sangat keras dengan System.ComponentModel.TypeDescriptor tanpa hasil. Itu tidak berarti itu tidak bisa berfungsi tetapi saya ingin melihat kode untuk itu.

Di bagian balasan, saya ingin mengubah beberapa nilai atribut. Saya melakukan 2 fungsi yang berfungsi dengan baik untuk tujuan itu.

        // ************************************************************************
        public static void SetObjectPropertyDescription(this Type typeOfObject, string propertyName,  string description)
        {
            PropertyDescriptor pd = TypeDescriptor.GetProperties(typeOfObject)[propertyName];
            var att = pd.Attributes[typeof(DescriptionAttribute)] as DescriptionAttribute;
            if (att != null)
            {
                var fieldDescription = att.GetType().GetField("description", BindingFlags.NonPublic | BindingFlags.Instance);
                if (fieldDescription != null)
                {
                    fieldDescription.SetValue(att, description);
                }
            }
        }

        // ************************************************************************
        public static void SetPropertyAttributReadOnly(this Type typeOfObject, string propertyName, bool isReadOnly)
        {
            PropertyDescriptor pd = TypeDescriptor.GetProperties(typeOfObject)[propertyName];
            var att = pd.Attributes[typeof(ReadOnlyAttribute)] as ReadOnlyAttribute;
            if (att != null)
            {
                var fieldDescription = att.GetType().GetField("isReadOnly", BindingFlags.NonPublic | BindingFlags.Instance);
                if (fieldDescription != null)
                {
                    fieldDescription.SetValue(att, isReadOnly);
                }
            }
        }
Eric Ouellet
sumber