Tìm một lĩnh vực riêng tư với Reflection?


228

Cho lớp này

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

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

Tôi muốn tìm mục riêng _bar mà tôi sẽ đánh dấu bằng một thuộc tính. Điều đó có thể không?

Tôi đã thực hiện điều này với các thuộc tính nơi tôi đã tìm kiếm một thuộc tính, nhưng không bao giờ là trường thành viên riêng.

Các cờ ràng buộc mà tôi cần đặt để có được các trường riêng là gì?


@Nescio: Bạn có thể mở rộng lý do tại sao bạn sẽ thực hiện phương pháp đó không? ... những lợi ích? Hay chỉ đơn giản là ưu tiên? :)
Tôi chấp nhận

Câu trả lời:


279

Sử dụng BindingFlags.NonPublicBindingFlags.Instancecờ

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

11
Tôi chỉ có thể làm việc này bằng cách cung cấp cờ ràng buộc "BindingFlags.Instance".
Andy McClADE

1
Tôi đã sửa câu trả lời của bạn. Nó quá khó hiểu. Câu trả lời của Abe Heidebrarou là hoàn chỉnh nhất.
Lubos hasko

2
Hoạt động tuyệt vời - FYI VB.NET phiên bản Me.GetType (). GetFields (Reflection.BindingFlags.NonPublic Hoặc Reflection.BindingFlags.Instance)
gg.

2
Sử dụng cờ ràng buộc cá thể chỉ khi bạn muốn có được các phương thức cá thể. Nếu bạn muốn có một phương thức tĩnh riêng tư, bạn có thể sử dụng (BindingFlags.NonPublic | BindingFlags.Static)
ksun

166

Bạn có thể làm điều đó giống như với một tài sản:

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

9
Xin lỗi vì đăng bài cực kỳ khó chịu, nhưng điều này đã ném tôi đi. GetCustomAttribut (Type) sẽ không trả về null nếu không tìm thấy thuộc tính, nó chỉ trả về một mảng trống.
mất trí nhớ

42

Nhận giá trị của biến riêng bằng Reflection:

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

Đặt giá trị cho biến riêng bằng Reflection:

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

Trong đó objectForFooClass là một thể hiện không null cho loại lớp Foo.


Câu trả lời tương tự mô tả chức năng dễ sử dụng GetInstanceField(typeof(YourClass), instance, "someString") as string Làm thế nào để có được giá trị của trường riêng trong C #?
Michael Freidgeim

24

Một điều mà bạn cần lưu ý khi phản ánh về các thành viên tư nhân là nếu ứng dụng của bạn đang chạy ở mức tin cậy trung bình (ví dụ như khi bạn đang chạy trên môi trường lưu trữ được chia sẻ), thì nó sẽ không tìm thấy họ - Tùy chọn BindingFlags.NonPublic sẽ bị bỏ qua.


jammycakes bạn có thể vui lòng cho một ví dụ về môi trường lưu trữ được chia sẻ? Tôi đang nghĩ rằng iis với nhiều ứng dụng là những gì bạn đang nhận được?
Brian Sweeney

Tôi đang nói về nơi IIS bị khóa để tin tưởng một phần ở cấp độ machine.config. Bạn thường chỉ tìm thấy điều này trên các gói lưu trữ web chia sẻ rẻ tiền và khó chịu trong những ngày này (những thứ tôi không còn sử dụng nữa) - nếu bạn có toàn quyền kiểm soát máy chủ của mình thì nó sẽ không thực sự phù hợp vì tin tưởng hoàn toàn là mặc định.
jammycakes

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

Tôi sẽ không biết tên của lĩnh vực này. Tôi muốn tìm nó mà không có tên và khi thuộc tính nằm trên nó.
David Basarab

Để tìm tên trường, thật dễ dàng thực hiện trong Visual Studio. Đặt điểm dừng tại biến, xem các trường của nó (bao gồm cả riêng tư, thường được bắt đầu bằng m_fieldname). Thay m_fieldname đó vào lệnh trên.
Hao Nguyen

13

Cú pháp đẹp với phương pháp mở rộng

Bạn có thể truy cập bất kỳ trường riêng nào thuộc loại tùy ý với mã như thế này:

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

Cho rằng bạn cần xác định một phương thức mở rộng sẽ thực hiện công việc cho bạn:

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
Anh bạn, đây là HOÀN HẢO để truy cập vào một biến được bảo vệ mà không để lộ nó ra NLua trong mã của tôi! Tuyệt vời!
tayoung

6

Tôi sử dụng phương pháp này cá nhân

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

6

Dưới đây là một số phương thức mở rộng để nhận và đặt các thuộc tính và trường riêng tư (thuộc tính với setter):

ví dụ sử dụng:

    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

Mã số:

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

Có, tuy nhiên, bạn sẽ cần đặt các cờ Binding của mình để tìm kiếm các trường riêng tư (nếu bạn đang tìm kiếm thành viên bên ngoài thể hiện của lớp).

Cờ ràng buộc bạn sẽ cần là: System.Reflection.BindingFlags.NonPublic


2

Tôi đã xem qua cái này trong khi tìm kiếm cái này trên google vì vậy tôi nhận ra mình đang viết một bài cũ. Tuy nhiên, GetCustomAttribut yêu cầu hai thông số.

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

Tham số thứ hai chỉ định xem bạn có muốn tìm kiếm phân cấp thừa kế hay không

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.