Penyembunyian dan refleksi properti (C#)

Mendeklarasikan properti di kelas turunan yang cocok dengan nama properti di kelas dasar akan "menyembunyikannya" (kecuali properti tersebut menimpanya dengan kata kunci override). Properti kelas dasar dan turunan akan dikembalikan oleh Type.GetProperties() jika tipenya tidak cocok. Namun, jika tipenya lakukan cocok, yang mengejutkan hanya properti kelas turunan yang dikembalikan. Contohnya:

class A
{
    protected double p;
    public int P { get { return (int)p; } set { p = value; } }
}
class B : A
{
    public new int P { get { return (int)p; } set { p = value; } }
}
class C : B
{
    public new float P { get { return (float)p; } set { p = value; } }
}

Memanggil typeof(C).GetProperties() hanya akan mengembalikan B.P dan C.P. Apakah mungkin memanggil GetProperties() dengan cara mengembalikan ketiganya? Hampir pasti ada cara untuk melakukannya dengan menelusuri hierarki warisan, tetapi apakah ada solusi yang lebih bersih?


person Eric Mickelsen    schedule 26.04.2010    source sumber
comment
Mengapa Anda ingin memiliki hierarki kelas seperti itu?   -  person driis    schedule 26.04.2010
comment
@driis: Ini hanyalah contoh sepele untuk diilustrasikan. Namun, demi argumen tersebut, asumsikan hierarki kelas ini ditulis oleh orang lain selain saya, namun saya masih perlu menemukan semua properti publiknya, bahkan yang tersembunyi.   -  person Eric Mickelsen    schedule 26.04.2010
comment
Karena tidak mungkin menggunakan kelas turunan Anda dapat mengakses properti tersembunyi, saya kira secara efektif ia tidak memiliki properti untuk Refleksi. Anggap diri Anda beruntung, VB hanya menampilkan C.P saat menggunakan Shadows.   -  person Wilhelm    schedule 26.04.2010
comment
@Wilhelm: Tapi Anda dapat mengakses properti tersembunyi: C c = new C(); ((A)c).P = 42;   -  person Eric Mickelsen    schedule 26.04.2010
comment
Sudahkah Anda mencoba menggunakan BindingFlags.FlattenHierarchy? Meskipun demikian, tidak mengembalikan properti sepertinya merupakan perilaku yang benar, karena properti tersebut mungkin disembunyikan karena suatu alasan.   -  person Dan Bryant    schedule 26.04.2010
comment
@Dan: FlattenHierarchy ditujukan untuk anggota statis, tidak akan berfungsi di sini.   -  person Eric Mickelsen    schedule 26.04.2010
comment
Saya menyukai kedua jawaban sejauh ini dengan cukup baik. Saya telah memutuskan untuk memposting bounty dan memberikannya lebih banyak waktu untuk melihat apakah ada yang dapat memperbaikinya karena masing-masing memiliki kelemahan yang signifikan. Menariknya, baik bidang maupun metode tidak memiliki masalah refleksi ini. Beberapa penjelasan yang memuaskan untuk masalah ini mungkin juga merupakan jawaban yang dapat diterima.   -  person Eric Mickelsen    schedule 29.04.2010


Jawaban (3)


GetProperties didefinisikan sebagai semua properti publik dari tipe tersebut.

Anda bisa mendapatkan metode get dan set menggunakan:

typeof(C).GetMethods()
         .Where(m => m.Name.StartsWith("set_") || m.Name.StartsWith("get_"))

Meskipun ini sepertinya ide yang buruk, dibandingkan dengan menurunkan hierarki warisan untuk mendapatkan properti.

person Yuriy Faktorovich    schedule 26.04.2010
comment
Maka Anda akan memiliki masalah yang sama dengan metode get_P yang menyembunyikan salah satu induknya, menurut saya. - person driis; 26.04.2010
comment
@Yuriy: typeof(C).GetMethods().Where @driis: Tidak, sepertinya berfungsi dengan baik. - person Eric Mickelsen; 26.04.2010
comment
Kamu benar. Metode ini juga berhasil. Tidak begitu yakin mengapa ini berhasil, karena saya berasumsi aturan penyembunyian metode yang sama berlaku untuk metode dan juga properti. Hmm. - person driis; 26.04.2010
comment
Ini akan berhasil bagi saya jika saya bisa mendapatkan PropertyInfo dari pengambil atau penyetel. - person Eric Mickelsen; 26.04.2010
comment
Apa yang Anda perlukan dari PropertyInfo yang tidak disediakan oleh pengambil dan penyetel? - person Yuriy Faktorovich; 26.04.2010
comment
Saya kira ini akan berfungsi tanpa PropertyInfo, tetapi saya harus dapat secara efektif mencocokkan setiap pengambil dengan penyetelnya masing-masing, dan itu tampak berantakan. - person Eric Mickelsen; 26.04.2010
comment
Ini kurang bersih, tapi sejauh ini hanya ini metode satu-satunya yang benar-benar mengembalikan jumlah properti yang tepat. Oleh karena itu, ini adalah jawaban yang saya terima. Sungguh menakjubkan bagi saya bahwa refleksi dapat bekerja dengan sangat baik pada bidang dan metode, tetapi gagal dalam hal properti. - person Eric Mickelsen; 05.05.2010

Saya rasa hal ini tidak mungkin terjadi tanpa melintasi hierarki warisan. Namun, kodenya tidak harus terlalu banyak:

    public static IEnumerable<PropertyInfo> GetAllProperties(Type t)
    {
        while (t != typeof(object))
        {
            foreach (var prop in t.GetProperties(BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.Instance))
                yield return prop;
            t = t.BaseType;
        }
    }

Tentu saja, jika Anda mengetahui tipe dasar umum yang dapat Anda hentikan, daripada objeknya, itu akan lebih efisien. Juga; perlu beberapa waktu untuk melakukan refleksi, jadi simpan hasilnya. Lagi pula, informasi tipe tidak akan berubah selama eksekusi.

person driis    schedule 26.04.2010
comment
Bukankah ini akan menghasilkan properti yang diganti berkali-kali? - person Eric Mickelsen; 26.04.2010
comment
Tidak, karena dia mengatakan BindingFlags.DeclaredOnly. - person Yuriy Faktorovich; 26.04.2010
comment
Masalahnya di sini adalah jika kita menulis ulang hierarki kelas kita sehingga B.P mengesampingkan A.P, kita masih mendapatkan tiga properti meskipun objek bertipe C sekarang hanya memiliki dua. - person Eric Mickelsen; 26.04.2010
comment
Sebaliknya, Yuriy, jawaban Anda hanya akan menghasilkan dua set get/setter, yang merupakan perilaku yang benar. - person Eric Mickelsen; 26.04.2010
comment
Anda benar, saya salah membaca apa yang Anda katakan. Saya juga menguji metode saya dan hanya menghasilkan dua. - person Yuriy Faktorovich; 26.04.2010

Melalui refleksi, kata kunci new hanya menyembunyikan properti yang diwarisi jika tanda tangannya cocok. Saya kira refleksi cocok dengan tanda tangan pada pengakses properti (get_ & set_). Itulah alasan mengapa GetProperties() mengembalikan B.P dan C.P ketika jenis pengembaliannya berbeda.

Saya baru-baru ini menemukan Fasteflect yang menyediakan mekanisme refleksi tingkat lanjut.

Saya memeriksa dan Fasteflect type.Properties mengembalikan semua pohon anggota yang tersembunyi (P). Saya pikir API mempertimbangkan anggota pendukung (virtual/override) dan anggota tersembunyi (baru) secara berbeda yang merupakan hal yang baik untuk 'masalah' Anda;)

Tes saya dengan fastflect :

class Class1
{
    public object field1 = null;

    public virtual object Property1 { get; set; }

    public object Property2 { get; set; }

    public string Property3 { get; set; }
}

class Class2 : Class1
{
    public new object field1 = null;

    public override object Property1 { get; set; }

    public new string Property3 { get; set; }
}

class Class3 : Class2
{
    public new string Property3 { get; set; }
}

Filter anggota pendukung tetapi kembalikan semua anggota tersembunyi :

typeof(Class3).Properties(Flags.ExcludeBackingMembers | Flags.Public | Flags.Instance) 
  • typeof(Class3).Properties(Flags.ExcludeBackingMembers | Flags.Public | Flags.Instance) Count = 5 System.Collections.Generic.IList
    • [0] {System.String Property3} System.Reflection.PropertyInfo
    • [1] {System.Object Property1} System.Reflection.PropertyInfo
    • [2] {System.String Property3} System.Reflection.PropertyInfo
    • [3] {System.Object Property2} System.Reflection.PropertyInfo
    • [4] {System.String Property3} System.Reflection.PropertyInfo
person JoeBilly    schedule 04.05.2010
comment
msdn.microsoft.com/en-us/ perpustakaan/ : Konstanta, bidang, properti, atau tipe yang diperkenalkan di kelas atau struct menyembunyikan semua anggota kelas dasar dengan nama yang sama. - person Eric Mickelsen; 04.05.2010
comment
Bukan melalui refleksi. Karena properti adalah pengakses, saya kira refleksi cocok dengan tanda tangan pada metode pengakses (get_ & set_). - person JoeBilly; 04.05.2010
comment
Setelah beberapa pengujian dan pemeriksaan Fasterflect, saya dapat dengan aman mengatakan bahwa jawaban mereka terhadap masalah ini benar-benar setara dengan jawaban driis. Solusi rekursifnya untuk menemukan anggota tersembunyi tidak mampu mengecualikan properti virtual yang telah diganti. (Sebenarnya, menyetel tanda ExcludeBackingMembers mengecualikan ketiga properti dalam kedua kasus.) Namun, terima kasih atas tautannya. - person Eric Mickelsen; 04.05.2010
comment
Anehnya, saya menambahkan tes saya di postingan. Saya menguji lagi dan ExcludeBackingMembers tidak mengecualikan anggota yang tersembunyi. Saya mencoba tanpa properti otomatis juga: perilaku yang sama. - person JoeBilly; 05.05.2010
comment
Menarik - ini bekerja lebih baik dengan bendera yang Anda gunakan. Namun, itu masih gagal dalam pengujian saya. Jika Anda menyembunyikan properti yang diganti, properti tersebut tetap hilang. (Tambahkan objek baru publik Properti1 ke Kelas3 Anda.) Jadi, pada dasarnya, masih ada properti di sana yang dapat digunakan, namun tidak terlihat oleh refleksi. - person Eric Mickelsen; 05.05.2010
comment
Benar, menarik sekali, terima kasih atas komentarnya. Itu karena Properti1 di Kelas1 dan Kelas2 masih menjadi anggota pendukung. ExcludeBackingMembers melakukan tugasnya tetapi hampir buruk karena harus mengembalikan Properti1 dari Kelas2 juga (yang menjadi anggota tersembunyi) menurut pengubah baru di Kelas3. Mungkin kueri tingkat lanjut dapat menangani ini: fasterflect.codeplex.com/ - person JoeBilly; 05.05.2010
comment
Hm, Jika dengan: refleksi public int Prop { get; } dan public float Prop { get; } mengembalikan dua properti. Namun bila Anda memiliki kejadian dengan tipe berbeda maka refleksi hanya mengembalikan satu kejadian. Ini semua tampak seperti bug. - person Denis535; 14.10.2018