ค้นหาฟิลด์ส่วนตัวด้วยรีเฟลคชั่น


228

รับคลาสนี้

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

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

ฉันต้องการค้นหารายการส่วนตัว _bar ที่ฉันจะทำเครื่องหมายด้วยแอตทริบิวต์ เป็นไปได้ไหม

ฉันทำสิ่งนี้ด้วยคุณสมบัติที่ฉันได้ค้นหาแอ็ตทริบิวต์ แต่ไม่เคยมีเขตข้อมูลส่วนตัว

การตั้งค่าสถานะการเชื่อมโยงใดที่ฉันต้องตั้งค่าเพื่อรับฟิลด์ส่วนตัว


@Nescio: คุณสามารถขยายทำไมคุณจะใช้วิธีการที่? ... ประโยชน์ที่ได้รับ? หรือเพียงแค่การตั้งค่า? :)
IAbstract

คำตอบ:


279

ใช้BindingFlags.NonPublicและตั้งBindingFlags.Instanceค่าสถานะ

FieldInfo[] fields = myType.GetFields(
                         BindingFlags.NonPublic | 
                         BindingFlags.Instance);

11
ฉันสามารถทำให้สิ่งนี้ทำงานได้โดยการจัดหาแฟล็กการโยง "BindingFlags.Instance" ด้วย
Andy McCl เล็บ

1
ฉันได้แก้ไขคำตอบของคุณแล้ว มันสับสนเกินไป คำตอบของ Abe Heidebrecht นั้นสมบูรณ์แบบที่สุด
lubos hasko

2
ใช้งานได้ดี - รุ่น FYI VB.NET Me.GetType () GetFields (Reflection.BindingFlags.NonPublic หรือ Reflection.BindingFlags.Instance)
gg

2
การใช้การตั้งค่าสถานะการผูกอินสแตนซ์จะทำได้ก็ต่อเมื่อคุณต้องการได้รับวิธีการใช้งาน หากคุณต้องการได้รับวิธีคงที่ส่วนตัวคุณสามารถใช้ (BindingFlags.NonPublic | BindingFlags.Static)
ksun

166

คุณสามารถทำได้เช่นเดียวกับสถานที่ให้บริการ:

FieldInfo fi = typeof(Foo).GetField("_bar", BindingFlags.NonPublic | BindingFlags.Instance);
if (fi.GetCustomAttributes(typeof(SomeAttribute)) != null)
    ...

9
ขออภัยสำหรับการโพสต์ necro มาก แต่สิ่งนี้ก็โยนฉันออกไป GetCustomAttributes (Type) จะไม่ส่งคืนค่าว่างหากไม่พบแอตทริบิวต์มันจะส่งคืนอาร์เรย์ว่างเปล่า
ความจำเสื่อม

42

รับค่าของตัวแปรส่วนตัวโดยใช้ Reflection:

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

ตั้งค่าสำหรับตัวแปรส่วนตัวโดยใช้ Reflection:

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

โดยที่ objectForFooClass เป็นอินสแตนซ์ที่ไม่เป็นค่าว่างสำหรับประเภทคลาส Foo


คำตอบที่คล้ายกันอธิบายฟังก์ชั่นที่ใช้งานง่ายGetInstanceField(typeof(YourClass), instance, "someString") as string วิธีการรับค่าของสนามส่วนตัวใน C #?
Michael Freidgeim

24

สิ่งหนึ่งที่คุณต้องระวังเมื่อสะท้อนถึงสมาชิกส่วนตัวคือถ้าแอปพลิเคชันของคุณทำงานในระดับปานกลาง (เช่นเมื่อคุณใช้งานในสภาพแวดล้อมการโฮสต์ที่ใช้ร่วมกัน) จะไม่พบพวกเขา - BindingFlags.NonPublic ตัวเลือกจะถูกละเว้น


jammycakes คุณช่วยยกตัวอย่างสภาพแวดล้อมการโฮสต์ร่วมกันได้ไหม? ฉันคิดว่ามีหลายแอพอยู่คุณกำลังได้อะไร
Brian Sweeney

ฉันกำลังพูดถึงว่า IIS ถูกล็อคไว้ที่ความเชื่อถือบางส่วนที่ระดับ machine.config คุณมักจะพบสิ่งนี้ในแผนเว็บโฮสติ้งที่ใช้ร่วมกันราคาถูกและน่ารังเกียจวันนี้ (สิ่งที่ฉันไม่ได้ใช้อีกต่อไป) - หากคุณมีการควบคุมเซิร์ฟเวอร์ของคุณอย่างเต็มที่แล้วมันจะไม่เกี่ยวข้องกันจริงๆ ค่าเริ่มต้น.
jammycakes

18
typeof(MyType).GetField("fieldName", BindingFlags.NonPublic | BindingFlags.Instance)

ฉันจะไม่ทราบชื่อของฟิลด์ ฉันต้องการค้นหาโดยไม่มีชื่อและเมื่อแอตทริบิวต์นั้นอยู่
David Basarab

หากต้องการค้นหาชื่อฟิลด์คุณสามารถทำได้ง่ายใน Visual Studio ตั้งค่าเบรกพอยต์ที่ตัวแปรดูฟิลด์ของมัน (รวมถึงไพรเวตซึ่งมักเริ่มต้นด้วย m_fieldname) แทนที่ m_fieldname นั้นในคำสั่งด้านบน
Hao Nguyen

13

ไวยากรณ์ที่ดีด้วยวิธีการขยาย

คุณสามารถเข้าถึงฟิลด์ส่วนตัวของประเภทใดก็ได้โดยใช้รหัสดังนี้:

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

เพื่อที่คุณจะต้องกำหนดวิธีการขยายที่จะทำงานให้คุณ:

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);
    }
}

1
เพื่อนนี่คือที่สมบูรณ์แบบสำหรับการเข้าถึงตัวแปรที่มีการป้องกันโดยไม่ต้องเปิดเผยให้กับ NLua ในรหัสของฉัน! ! น่ากลัว
tayoung

6

ฉันใช้วิธีนี้เป็นการส่วนตัว

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

6

ต่อไปนี้เป็นวิธีส่วนขยายสำหรับการรับและตั้งค่าฟิลด์ส่วนตัวและคุณสมบัติ (คุณสมบัติที่มี setter):

ตัวอย่างการใช้งาน:

    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

รหัส:

    /// <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;
        }
    }

4

ใช่ แต่คุณจะต้องตั้งค่าสถานะ Binding เพื่อค้นหาเขตข้อมูลส่วนตัว (หากคุณกำลังมองหาสมาชิกนอกอินสแตนซ์ของคลาส)

ธงที่มีผลผูกพันที่คุณจะต้องมีคือ: System.Reflection.BindingFlags.NonPublic


2

ฉันเจอสิ่งนี้ในขณะที่ค้นหาสิ่งนี้บน google ดังนั้นฉันจึงรู้ว่าฉันกำลังโพสต์ข้อความเก่า ๆ อย่างไรก็ตาม GetCustomAttributes ต้องใช้สองพารามิเตอร์

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

พารามิเตอร์ที่สองระบุว่าคุณต้องการค้นหาลำดับชั้นการสืบทอดหรือไม่

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.