Temukan bidang pribadi dengan Reflection?

228

Diberikan kelas ini

class Foo
{
    // Want to find _bar with reflection
    [SomeAttribute]
    private string _bar;

    public string BigBar
    {
        get { return this._bar; }
    }
}

Saya ingin mencari item pribadi _bar yang akan saya tandai dengan atribut. Apakah itu mungkin?

Saya telah melakukan ini dengan properti di mana saya telah mencari atribut, tetapi tidak pernah bidang anggota pribadi.

Apa bendera pengikat yang harus saya atur untuk mendapatkan bidang pribadi?

David Basarab
sumber
@Nescio: Bisakah Anda memperluas alasan mengapa Anda mengambil pendekatan itu? ...keuntungan-keuntungan? Atau hanya preferensi? :)
IAbstract

Jawaban:

279

Gunakan BindingFlags.NonPublicdan BindingFlags.Instancepanji

FieldInfo[] fields = myType.GetFields(
                         BindingFlags.NonPublic | 
                         BindingFlags.Instance);
Bob King
sumber
11
Saya hanya bisa mendapatkan ini untuk bekerja dengan juga memasok bendera mengikat "BindingFlags.Instance".
Andy McClrolley
1
Saya telah memperbaiki jawaban Anda. Sebaliknya terlalu membingungkan. Namun jawaban Abe Heidebrecht adalah yang paling lengkap.
lubos hasko
2
Berfungsi bagus - FYI VB.NET versi Me.GetType (). GetFields (Reflection.BindingFlags.NonPublic Atau Reflection.BindingFlags.Instance)
gg.
2
Menggunakan flag pengikat instan hanya jika Anda ingin mendapatkan metode instan. Jika Anda ingin mendapatkan metode statis privat, Anda dapat menggunakan (BindingFlags.NonPublic | BindingFlags.Static)
ksun
166

Anda dapat melakukannya seperti halnya dengan properti:

FieldInfo fi = typeof(Foo).GetField("_bar", BindingFlags.NonPublic | BindingFlags.Instance);
if (fi.GetCustomAttributes(typeof(SomeAttribute)) != null)
    ...
Abe Heidebrecht
sumber
9
Maaf untuk necro-postingan ekstrim, tapi ini membuat saya kecewa. GetCustomAttributes (Type) tidak akan mengembalikan nol jika atribut tidak ditemukan, itu hanya mengembalikan array kosong.
amnesia
42

Dapatkan nilai variabel pribadi menggunakan Refleksi:

var _barVariable = typeof(Foo).GetField("_bar", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(objectForFooClass);

Tetapkan nilai untuk variabel pribadi menggunakan Refleksi:

typeof(Foo).GetField("_bar", BindingFlags.NonPublic | BindingFlags.Instance).SetValue(objectForFoocClass, "newValue");

Di mana objectForFooClass adalah turunan bukan nol untuk tipe kelas Foo.

Suriya
sumber
Jawaban serupa menjelaskan fungsi yang mudah digunakan. GetInstanceField(typeof(YourClass), instance, "someString") as string Bagaimana mendapatkan nilai bidang pribadi dalam C #?
Michael Freidgeim
24

Satu hal yang perlu Anda perhatikan ketika merefleksikan anggota pribadi adalah bahwa jika aplikasi Anda berjalan dalam kepercayaan sedang (seperti, misalnya, ketika Anda berjalan di lingkungan hosting bersama), ia tidak akan menemukan mereka - yang Opsi BindingFlags.NonPublic hanya akan diabaikan.

jammycakes
sumber
jammycakes dapatkah Anda memberikan contoh lingkungan hosting bersama? Saya pikir iis dengan beberapa aplikasi adalah apa yang Anda dapatkan?
Brian Sweeney
Saya berbicara tentang di mana IIS dikunci untuk kepercayaan parsial di tingkat machine.config. Anda biasanya hanya menemukan ini pada paket hosting web murah dan tidak menyenangkan akhir-akhir ini (yang tidak lagi saya gunakan) - jika Anda memiliki kontrol penuh atas server Anda maka itu tidak akan benar-benar relevan karena kepercayaan penuh adalah standar.
jammycakes
18
typeof(MyType).GetField("fieldName", BindingFlags.NonPublic | BindingFlags.Instance)
Darren Kopp
sumber
Saya tidak akan tahu nama bidangnya. Saya ingin menemukannya tanpa nama dan ketika atribut ada di sana.
David Basarab
Untuk menemukan nama bidang, mudah dilakukan di Visual Studio. Tetapkan breakpoint pada variabel, lihat bidangnya (termasuk pribadi, biasanya dimulai dengan m_fieldname). Ganti m_fieldname itu ke perintah di atas.
Hao Nguyen
13

Sintaks Bagus Dengan Metode Ekstensi

Anda dapat mengakses bidang pribadi apa pun dari jenis sewenang-wenang dengan kode seperti ini:

Foo foo = new Foo();
string c = foo.GetFieldValue<string>("_bar");

Untuk itu Anda perlu mendefinisikan metode ekstensi yang akan bekerja untuk Anda:

public static class ReflectionExtensions {
    public static T GetFieldValue<T>(this object obj, string name) {
        // Set the flags so that private and public fields from instances will be found
        var bindingFlags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance;
        var field = obj.GetType().GetField(name, bindingFlags);
        return (T)field?.GetValue(obj);
    }
}
Bruno Zell
sumber
1
Bung, ini SEMPURNA untuk mengakses variabel yang dilindungi tanpa mengeksposnya ke NLua dalam kode saya! Luar biasa!
tayoung
6

Saya menggunakan metode ini secara pribadi

if (typeof(Foo).GetFields(BindingFlags.NonPublic | BindingFlags.Instance).Any(c => c.GetCustomAttributes(typeof(SomeAttribute), false).Any()))
{ 
    // do stuff
}
sa_ddam213
sumber
6

Berikut adalah beberapa metode ekstensi untuk mendapatkan dan mengatur bidang dan properti pribadi secara sederhana (properti dengan setter):

contoh penggunaan:

    public class Foo
    {
        private int Bar = 5;
    }

    var targetObject = new Foo();
    var barValue = targetObject.GetMemberValue("Bar");//Result is 5
    targetObject.SetMemberValue("Bar", 10);//Sets Bar to 10

Kode:

    /// <summary>
    /// Extensions methos for using reflection to get / set member values
    /// </summary>
    public static class ReflectionExtensions
    {
        /// <summary>
        /// Gets the public or private member using reflection.
        /// </summary>
        /// <param name="obj">The source target.</param>
        /// <param name="memberName">Name of the field or property.</param>
        /// <returns>the value of member</returns>
        public static object GetMemberValue(this object obj, string memberName)
        {
            var memInf = GetMemberInfo(obj, memberName);

            if (memInf == null)
                throw new System.Exception("memberName");

            if (memInf is System.Reflection.PropertyInfo)
                return memInf.As<System.Reflection.PropertyInfo>().GetValue(obj, null);

            if (memInf is System.Reflection.FieldInfo)
                return memInf.As<System.Reflection.FieldInfo>().GetValue(obj);

            throw new System.Exception();
        }

        /// <summary>
        /// Gets the public or private member using reflection.
        /// </summary>
        /// <param name="obj">The target object.</param>
        /// <param name="memberName">Name of the field or property.</param>
        /// <returns>Old Value</returns>
        public static object SetMemberValue(this object obj, string memberName, object newValue)
        {
            var memInf = GetMemberInfo(obj, memberName);


            if (memInf == null)
                throw new System.Exception("memberName");

            var oldValue = obj.GetMemberValue(memberName);

            if (memInf is System.Reflection.PropertyInfo)
                memInf.As<System.Reflection.PropertyInfo>().SetValue(obj, newValue, null);
            else if (memInf is System.Reflection.FieldInfo)
                memInf.As<System.Reflection.FieldInfo>().SetValue(obj, newValue);
            else
                throw new System.Exception();

            return oldValue;
        }

        /// <summary>
        /// Gets the member info
        /// </summary>
        /// <param name="obj">source object</param>
        /// <param name="memberName">name of member</param>
        /// <returns>instanse of MemberInfo corresponsing to member</returns>
        private static System.Reflection.MemberInfo GetMemberInfo(object obj, string memberName)
        {
            var prps = new System.Collections.Generic.List<System.Reflection.PropertyInfo>();

            prps.Add(obj.GetType().GetProperty(memberName,
                                               System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance |
                                               System.Reflection.BindingFlags.FlattenHierarchy));
            prps = System.Linq.Enumerable.ToList(System.Linq.Enumerable.Where( prps,i => !ReferenceEquals(i, null)));
            if (prps.Count != 0)
                return prps[0];

            var flds = new System.Collections.Generic.List<System.Reflection.FieldInfo>();

            flds.Add(obj.GetType().GetField(memberName,
                                            System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance |
                                            System.Reflection.BindingFlags.FlattenHierarchy));

            //to add more types of properties

            flds = System.Linq.Enumerable.ToList(System.Linq.Enumerable.Where(flds, i => !ReferenceEquals(i, null)));

            if (flds.Count != 0)
                return flds[0];

            return null;
        }

        [System.Diagnostics.DebuggerHidden]
        private static T As<T>(this object obj)
        {
            return (T)obj;
        }
    }
epsi1on
sumber
4

Ya, namun Anda perlu mengatur bendera Binding Anda untuk mencari bidang pribadi (jika Anda mencari anggota di luar instance kelas).

Bendera mengikat yang Anda perlukan adalah: System.Reflection.BindingFlags.NonPublic

mmattax
sumber
2

Saya menemukan ini saat mencari ini di google jadi saya menyadari saya menabrak posting lama. Namun GetCustomAttributes membutuhkan dua params.

typeof(Foo).GetFields(BindingFlags.NonPublic | BindingFlags.Instance)
.Where(x => x.GetCustomAttributes(typeof(SomeAttribute), false).Length > 0);

Parameter kedua menentukan apakah Anda ingin mencari hierarki warisan atau tidak

Gunner
sumber