簡體   English   中英

使用反射找到一個私有字段?

[英]Find a private field with Reflection?

鑒於這個類

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

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

我想找到我將用屬性標記的私有項目 _bar。 那可能嗎?

我已經在我尋找屬性的屬性中完成了此操作,但從未尋找私有成員字段。

我需要設置哪些綁定標志來獲取私有字段?

使用BindingFlags.NonPublicBindingFlags.Instance標志

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

您可以像使用屬性一樣執行此操作:

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

使用反射獲取私有變量的值:

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

使用反射設置私有變量的值:

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

其中 objectForFooClass 是類類型 Foo 的非空實例。

帶有擴展方法的好語法

您可以使用如下代碼訪問任意類型的任何私有字段:

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

在考慮私有成員時需要注意的一件事是,如果您的應用程序以中等信任度運行(例如,當您在共享托管環境中運行時),它將找不到它們—— BindingFlags.NonPublic 選項將被忽略。

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

我個人使用這種方法

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

下面是一些用於簡單獲取和設置私有字段和屬性(帶有 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;
        }
    }

是的,但是您需要設置綁定標志來搜索私有字段(如果您在類實例之外尋找成員)。

您需要的綁定標志是:System.Reflection.BindingFlags.NonPublic

我在谷歌上搜索這個時遇到了這個,所以我意識到我正在撞一個舊帖子。 但是 GetCustomAttributes 需要兩個參數。

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

第二個參數指定是否要搜索繼承層次結構

如果您的 .Net 框架大於 4.5。 您可以使用 GetRuntimeFields 方法。

此方法返回在指定類型上定義的所有字段,包括繼承、非公共、實例和靜態字段。

https://docs.microsoft.com/en-us/dotnet/api/system.reflection.runtimereflectionextensions.getruntimefields?view=net-6.0

var foo = new Foo();
var fooFields = foo.GetType().GetRuntimeFields()

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM