Encontre um campo particular com o Reflection?

228

Dada esta classe

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

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

Quero encontrar o item privado _bar que vou marcar com um atributo. Isso é possível?

Eu fiz isso com propriedades em que procurei um atributo, mas nunca um campo de membro privado.

Quais são os sinalizadores de ligação que eu preciso definir para obter os campos particulares?

David Basarab
fonte
@Nescio: Você pode expandir por que adotaria essa abordagem? ...Os benefícios? Ou simplesmente preferência? :)
IAbstract

Respostas:

279

Uso BindingFlags.NonPublice BindingFlags.Instancesinalizadores

FieldInfo[] fields = myType.GetFields(
                         BindingFlags.NonPublic | 
                         BindingFlags.Instance);
Bob King
fonte
11
Eu só consegui fazer isso funcionar fornecendo também o sinalizador de ligação "BindingFlags.Instance".
Andy McCl Bagagem
1
Eu consertei sua resposta. Caso contrário, é muito confuso. A resposta de Abe Heidebrecht foi a mais completa.
Lubos Hasko
2
Funciona muito bem - versão do FYI VB.NET Me.GetType (). GetFields (Reflection.BindingFlags.NonPublic Ou Reflection.BindingFlags.Instance)
gg.
2
O uso do sinalizador de ligação de instância é apenas se você deseja obter métodos de instância. Se você quisesse obter um método private static você pode usar (BindingFlags.NonPublic | BindingFlags.Static)
KSUN
166

Você pode fazer isso como em uma propriedade:

FieldInfo fi = typeof(Foo).GetField("_bar", BindingFlags.NonPublic | BindingFlags.Instance);
if (fi.GetCustomAttributes(typeof(SomeAttribute)) != null)
    ...
Abe Heidebrecht
fonte
9
Desculpe pela extrema necro-postagem, mas isso me assustou. GetCustomAttributes (Type) não retornará nulo se o atributo não for encontrado, ele simplesmente retorna uma matriz vazia.
Amnésia
42

Obtenha o valor da variável privada usando o Reflection:

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

Defina o valor para a variável privada usando o Reflection:

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

Onde objectForFooClass é uma instância não nula para o tipo de classe Foo.

Suriya
fonte
Resposta semelhante descreve a função fácil de usar GetInstanceField(typeof(YourClass), instance, "someString") as string Como obter o valor do campo privado em C #?
Michael Freidgeim
24

Uma coisa que você precisa estar ciente ao refletir sobre membros privados é que, se seu aplicativo estiver executando em confiança média (como, por exemplo, quando você estiver executando em um ambiente de hospedagem compartilhado), ele não os encontrará - o A opção BindingFlags.NonPublic será simplesmente ignorada.

jammycakes
fonte
jammycakes, você poderia dar um exemplo de ambiente de hospedagem compartilhada? Eu estou pensando que o iis com vários aplicativos é o que você está recebendo?
Brian Sweeney
Eu estou falando sobre onde o IIS está bloqueado para confiança parcial no nível machine.config. Atualmente, você normalmente encontra isso apenas em planos de hospedagem compartilhada baratos e desagradáveis ​​hoje em dia (como os que eu não uso mais) - se você tiver controle total sobre o servidor, ele não será relevante, pois a confiança total é a padrão.
Jammycakes #
18
typeof(MyType).GetField("fieldName", BindingFlags.NonPublic | BindingFlags.Instance)
Darren Kopp
fonte
Não saberei o nome do campo. Quero encontrá-lo sem o nome e quando o atributo estiver nele.
David Basarab
Para encontrar o nome do campo, é fácil fazer isso no Visual Studio. Defina o ponto de interrupção na variável, visualize seus campos (incluindo o privado, geralmente iniciado com m_fieldname). Substitua esse m_fieldname no comando acima.
Hao Nguyen
13

Sintaxe agradável com método de extensão

Você pode acessar qualquer campo privado de um tipo arbitrário com código como este:

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

Para isso, você precisa definir um método de extensão que fará o trabalho para você:

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
fonte
1
Cara, isso foi PERFEITO para acessar uma variável protegida sem expô-la ao NLua no meu código! Impressionante!
Tayoung
6

Eu uso esse método pessoalmente

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

Aqui estão alguns métodos de extensão para obter e definir campos e propriedades particulares simples (propriedades com o setter):

exemplo de uso:

    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

Código:

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

Sim, no entanto, você precisará definir seus sinalizadores de Ligação para procurar campos privados (se estiver procurando o membro fora da instância da classe).

O sinalizador de ligação que você precisará é: System.Reflection.BindingFlags.NonPublic

mmattax
fonte
2

Me deparei com isso enquanto procurava por isso no google, então percebo que estou encontrando um post antigo. No entanto, o GetCustomAttributes requer dois parâmetros.

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

O segundo parâmetro especifica se você deseja ou não pesquisar na hierarquia de herança

Artilheiro
fonte