c # foreach (propriedade no objeto)… Existe uma maneira simples de fazer isso?

92

Eu tenho uma classe que contém várias propriedades (todas são strings, se isso fizer alguma diferença).
Também tenho uma lista que contém muitas instâncias diferentes da classe.

Ao criar alguns testes de unidade para minhas classes, decidi que queria fazer um loop em cada objeto da lista e, em seguida, em cada propriedade desse objeto ...

Achei que fazer isso seria tão simples quanto ...

foreach (Object obj in theList)
{
     foreach (Property theProperties in obj)
     {
         do some stufff!!;
     }
}

Mas isso não funcionou! :( Recebo este erro ...

"A instrução foreach não pode operar em variáveis ​​do tipo 'Application.Object' porque 'Application.Object' não contém uma definição pública para 'GetEnumerator'"

Alguém conhece uma maneira de fazer isso sem toneladas de ifs e loops ou sem entrar em nada muito complexo?

Jammerz858
fonte
5
No futuro, não diga "Não funciona" em uma pergunta. Em vez disso, especifique o problema que você está tendo (erro do compilador, etc.). Obrigado!
Robert Harvey
2
Atualizada! Obrigado pelo
aviso

Respostas:

143

Experimente:

foreach (PropertyInfo propertyInfo in obj.GetType().GetProperties())
{
   // do stuff here
}

Observe também que Type.GetProperties()tem uma sobrecarga que aceita um conjunto de sinalizadores de ligação para que você possa filtrar propriedades em um critério diferente, como nível de acessibilidade, consulte MSDN para obter mais detalhes: Método Type.GetProperties (BindingFlags) Por último, mas não menos importante, não se esqueça de adicione a referência de montagem "system.Reflection".

Por exemplo, para resolver todas as propriedades públicas:

foreach (var propertyInfo in obj.GetType()
                                .GetProperties(
                                        BindingFlags.Public 
                                        | BindingFlags.Instance))
{
   // do stuff here
}

Informe se isso funciona conforme o esperado.

sll
fonte
4
Depois de controlar as propriedades dos objetos, há alguma maneira de obter o valor de cada propriedade? ou seja, um nome ou código postal, por exemplo
JsonStatham
35

Você pode percorrer todas as propriedades não indexadas de um objeto como este:

var s = new MyObject();
foreach (var p in s.GetType().GetProperties().Where(p => !p.GetGetMethod().GetParameters().Any())) {
    Console.WriteLine(p.GetValue(s, null));
}

Uma vez que GetProperties()retorna indexadores , bem como propriedades simples, você precisa de um filtro adicional antes de chamar GetValuepara saber se é seguro passar nullcomo o segundo parâmetro.

Pode ser necessário modificar ainda mais o filtro para eliminar propriedades somente gravação e outras inacessíveis.

dasblinkenlight
fonte
+1 para fazer algo apenas com as propriedades que podem realmente ser usadas - você também pode filtrar as propriedades somente gravação.
@hvd É um excelente ponto nas propriedades somente gravação! Quase me esqueci deles. Meu código travará se encontrar uma propriedade com nullgetter, mas tenho certeza que o OP descobrirá como obter apenas as propriedades de que precisa.
dasblinkenlight
27

Você está quase lá, você só precisa obter as propriedades do tipo, em vez de esperar que as propriedades estejam acessíveis na forma de uma coleção ou bolsa de propriedades:

var property in obj.GetType().GetProperties()

De lá, você pode acessar da seguinte forma :

property.Name
property.GetValue(obj, null)

Com GetValueo segundo parâmetro permitirá que você especifique valores de índice, que funcionarão com propriedades que retornam coleções - uma vez que uma string é uma coleção de caracteres, você também pode especificar um índice para retornar um caractere se necessário.

Grant Thomas
fonte
1
Eu chateei alguém? Estou aberto para saber o que há de errado nisso, caso contrário, posso nunca aprender.
Grant Thomas
Você provavelmente obteve o downvote quando seu código estava errado (antes de sua edição ninja).
Robert Harvey
20

Claro, sem problema:

foreach(object item in sequence)
{
    if (item == null) continue;
    foreach(PropertyInfo property in item.GetType().GetProperties())
    {
        // do something with the property
    }
}
Eric Lippert
fonte
por que você adicionou esta linha? if (item == null) continue;Pessoalmente, acho que se você obtiver um objeto nulo nesse ponto, algo deu errado muito antes e é aí que a validação deveria estar, ou estou errado?
Rafael Herscovici
@Dementic: Claro, podemos dizer que a sequência deve conter referências não nulas.
Eric Lippert
3

Use o Reflection para fazer isso

SomeClass A = SomeClass(...)
PropertyInfo[] properties = A.GetType().GetProperties();
Arion
fonte
2

Procurei a resposta para uma pergunta semelhante nesta página, escrevi as respostas para várias perguntas semelhantes que podem ajudar as pessoas que entram nesta página.

Lista de classes

A classe List <T> representa a lista de objetos que podem ser acessados ​​por índice. Ele vem com o namespace System.Collection.Generic. A classe de lista pode ser usada para criar uma coleção de diferentes tipos, como inteiros, strings, etc. A classe de lista também fornece os métodos para pesquisar, classificar e manipular listas.

Classe com propriedade :

class TestClss
{
    public string id { set; get; }
    public string cell1 { set; get; }
    public string cell2 { set; get; }
}
var MyArray = new List<TestClss> {
    new TestClss() { id = "1", cell1 = "cell 1 row 1 Data", cell2 = "cell 2 row 1 Data" },
    new TestClss() { id = "2", cell1 = "cell 1 row 2 Data", cell2 = "cell 2 row 2 Data" },
    new TestClss() { id = "3", cell1 = "cell 1 row 2 Data", cell2 = "cell 2 row 3 Data" }
};
foreach (object Item in MyArray)
{
    Console.WriteLine("Row Start");
    foreach (PropertyInfo property in Item.GetType().GetProperties())
    {
        var Key = property.Name;
        var Value = property.GetValue(Item, null);
        Console.WriteLine("{0}={1}", Key, Value);
    }
}

OU, Classe com campo :

class TestClss
{
    public string id = "";
    public string cell1 = "";
    public string cell2 = "";
}
var MyArray = new List<TestClss> {
    new TestClss() { id = "1", cell1 = "cell 1 row 1 Data", cell2 = "cell 2 row 1 Data" },
    new TestClss() { id = "2", cell1 = "cell 1 row 2 Data", cell2 = "cell 2 row 2 Data" },
    new TestClss() { id = "3", cell1 = "cell 1 row 2 Data", cell2 = "cell 2 row 3 Data" }
};
foreach (object Item in MyArray)
{
    Console.WriteLine("Row Start");
    foreach (var fieldInfo in Item.GetType().GetFields())
    {
        var Key = fieldInfo.Name;
        var Value = fieldInfo.GetValue(Item);
    }

}

OU, Lista de objetos (sem as mesmas células):

var MyArray = new List<object> {
    new { id = "1", cell1 = "cell 1 row 1 Data", cell2 = "cell 2 row 1 Data" },
    new { id = "2", cell1 = "cell 1 row 2 Data", cell2 = "cell 2 row 2 Data" },
    new { id = "3", cell1 = "cell 1 row 2 Data", cell2 = "cell 2 row 3 Data", anotherCell = "" }
};
foreach (object Item in MyArray)
{
    Console.WriteLine("Row Start");
    foreach (var props in Item.GetType().GetProperties())
    {
        var Key = props.Name;
        var Value = props.GetMethod.Invoke(Item, null).ToString();
        Console.WriteLine("{0}={1}", Key, Value);
    }
}

OU, Lista de objetos (deve ter as mesmas células):

var MyArray = new[] {
    new { id = "1", cell1 = "cell 1 row 1 Data", cell2 = "cell 2 row 1 Data" },
    new { id = "2", cell1 = "cell 1 row 2 Data", cell2 = "cell 2 row 2 Data" },
    new { id = "3", cell1 = "cell 1 row 2 Data", cell2 = "cell 2 row 3 Data" }
};
foreach (object Item in MyArray)
{
    Console.WriteLine("Row Start");
    foreach (var props in Item.GetType().GetProperties())
    {
        var Key = props.Name;
        var Value = props.GetMethod.Invoke(Item, null).ToString();
        Console.WriteLine("{0}={1}", Key, Value);
    }
}

OU, Lista de objetos (com chave):

var MyArray = new {
    row1 = new { id = "1", cell1 = "cell 1 row 1 Data", cell2 = "cell 2 row 1 Data" },
    row2 = new { id = "2", cell1 = "cell 1 row 2 Data", cell2 = "cell 2 row 2 Data" },
    row3 = new { id = "3", cell1 = "cell 1 row 2 Data", cell2 = "cell 2 row 3 Data" }
};
// using System.ComponentModel;  for TypeDescriptor
foreach (PropertyDescriptor Item in TypeDescriptor.GetProperties(MyArray))
{
    string Rowkey = Item.Name;
    object RowValue = Item.GetValue(MyArray);
    Console.WriteLine("Row key is: {0}", Rowkey);
    foreach (var props in RowValue.GetType().GetProperties())
    {
        var Key = props.Name;
        var Value = props.GetMethod.Invoke(RowValue, null).ToString();
        Console.WriteLine("{0}={1}", Key, Value);
    }
}

OU, lista de dicionário

var MyArray = new List<Dictionary<string, string>>() {
    new Dictionary<string, string>() { { "id", "1" }, { "cell1", "cell 1 row 1 Data" }, { "cell2", "cell 2 row 1 Data" } },
    new Dictionary<string, string>() { { "id", "2" }, { "cell1", "cell 1 row 2 Data" }, { "cell2", "cell 2 row 2 Data" } },
    new Dictionary<string, string>() { { "id", "3" }, { "cell1", "cell 1 row 3 Data" }, { "cell2", "cell 2 row 3 Data" } }
};
foreach (Dictionary<string, string> Item in MyArray)
{
    Console.WriteLine("Row Start");
    foreach (KeyValuePair<string, string> props in Item)
    {
        var Key = props.Key;
        var Value = props.Value;
        Console.WriteLine("{0}={1}", Key, Value);
    }
}

Boa sorte..

mirzaei.sajad
fonte
0

Uma pequena palavra de cautela, se "fazer alguma coisa" significa atualizar o valor da propriedade real que você visita E se houver uma propriedade do tipo de estrutura ao longo do caminho do objeto raiz para a propriedade visitada, a alteração que você fez na propriedade não ser refletido no objeto raiz.

Dogu Arslan
fonte
0

Não consegui fazer com que nenhuma das formas acima funcionasse, mas funcionou. O nome de usuário e a senha para DirectoryEntry são opcionais.

   private List<string> getAnyDirectoryEntryPropertyValue(string userPrincipalName, string propertyToSearchFor)
    {
        List<string> returnValue = new List<string>();
        try
        {
            int index = userPrincipalName.IndexOf("@");
            string originatingServer = userPrincipalName.Remove(0, index + 1);
            string path = "LDAP://" + originatingServer; //+ @"/" + distinguishedName;
            DirectoryEntry objRootDSE = new DirectoryEntry(path, PSUsername, PSPassword);
            var objSearcher = new System.DirectoryServices.DirectorySearcher(objRootDSE);
            objSearcher.Filter = string.Format("(&(UserPrincipalName={0}))", userPrincipalName);
            SearchResultCollection properties = objSearcher.FindAll();

            ResultPropertyValueCollection resPropertyCollection = properties[0].Properties[propertyToSearchFor];
            foreach (string resProperty in resPropertyCollection)
            {
                returnValue.Add(resProperty);
            }
        }
        catch (Exception ex)
        {
            returnValue.Add(ex.Message);
            throw;
        }

        return returnValue;
    }
Bbb
fonte
0

Uma solução de copiar e colar (métodos de extensão) principalmente com base em respostas anteriores a esta pergunta.

Também lida corretamente com IDicitonary (ExpandoObject / dynamic), que geralmente é necessário ao lidar com essas coisas refletidas.

Não recomendado para uso em loops apertados e outros caminhos quentes. Nesses casos, você precisará de alguma compilação de cache / emissão de IL / árvore de expressão.

    public static IEnumerable<(string Name, object Value)> GetProperties(this object src)
    {
        if (src is IDictionary<string, object> dictionary)
        {
            return dictionary.Select(x => (x.Key, x.Value));
        }
        return src.GetObjectProperties().Select(x => (x.Name, x.GetValue(src)));
    }

    public static IEnumerable<PropertyInfo> GetObjectProperties(this object src)
    {
        return src.GetType()
            .GetProperties(BindingFlags.Public | BindingFlags.Instance)
            .Where(p => !p.GetGetMethod().GetParameters().Any());
    }
Zar Shardan
fonte