c # foreach (propriété dans l'objet)… Y a-t-il un moyen simple de faire cela?


92

J'ai une classe contenant plusieurs propriétés (toutes sont des chaînes si cela fait une différence).
J'ai également une liste, qui contient de nombreuses instances différentes de la classe.

Lors de la création de tests unitaires pour mes classes, j'ai décidé que je voulais parcourir chaque objet de la liste, puis parcourir chaque propriété de cet objet ...

Je pensais que faire cela serait aussi simple que ...

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

Mais cela n'a pas fonctionné! :( J'obtiens cette erreur ...

"L'instruction foreach ne peut pas fonctionner sur des variables de type 'Application.Object' car 'Application.Object' ne contient pas de définition publique pour 'GetEnumerator'"

Quelqu'un connaît-il un moyen de faire cela sans des tonnes de ifs et de boucles ou sans entrer dans quelque chose de trop complexe?


5
À l'avenir, veuillez ne pas dire "Cela ne fonctionne pas" dans une question. Au lieu de cela, spécifiez le problème que vous rencontrez (erreur du compilateur, etc.). Merci!
Robert Harvey

2
Actualisé! Merci pour la tête Robert
Jammerz858

Réponses:


143

Essayez ceci:

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

Veuillez également noter qu'il Type.GetProperties()a une surcharge qui accepte un ensemble d'indicateurs de liaison afin que vous puissiez filtrer les propriétés sur un critère différent comme le niveau d'accessibilité, voir MSDN pour plus de détails: Type.GetProperties, méthode (BindingFlags) Dernier point mais non le moindre, n'oubliez pas de ajoutez la référence d'assemblage "system.Reflection".

Par exemple pour résoudre toutes les propriétés publiques:

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

Veuillez me faire savoir si cela fonctionne comme prévu.


4
Une fois que vous avez une poignée sur les propriétés des objets, est-il possible de saisir la valeur de chaque propriété? c'est-à-dire un nom ou un code postal par exemple
JsonStatham

35

Vous pouvez parcourir toutes les propriétés non indexées d'un objet comme ceci:

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

Puisque GetProperties()renvoie des indexeurs ainsi que des propriétés simples, vous avez besoin d'un filtre supplémentaire avant d'appeler GetValuepour savoir qu'il est sûr de passer nullcomme deuxième paramètre.

Vous devrez peut-être modifier davantage le filtre afin d'éliminer les propriétés en écriture seule et autrement inaccessibles.


+1 pour ne faire quelque chose avec les propriétés qui peuvent réellement être utilisées - vous pouvez également filtrer les propriétés en écriture seule.

@hvd C'est un excellent point sur les propriétés en écriture seule! Je les ai presque oubliés. Mon code plantera s'il rencontre une propriété avec nullgetter, mais je suis sûr qu'OP découvrira comment obtenir uniquement les propriétés dont il a besoin.
dasblinkenlight

27

Vous y êtes presque, il vous suffit d'obtenir les propriétés du type, plutôt que de vous attendre à ce que les propriétés soient accessibles sous la forme d'une collection ou d'un sac de propriétés:

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

De là, vous pouvez accéder comme suit :

property.Name
property.GetValue(obj, null)

Le GetValuedeuxième paramètre vous permettra de spécifier des valeurs d'index, qui fonctionneront avec les propriétés retournant des collections - puisqu'une chaîne est une collection de caractères, vous pouvez également spécifier un index pour renvoyer un caractère si nécessaire.


1
Ai-je dérangé quelqu'un? Je suis ouvert à savoir ce qui ne va pas à ce sujet, sinon je n'apprendrai peut-être jamais.
Grant Thomas

Vous avez probablement obtenu le vote négatif lorsque votre code était faux (avant votre modification ninja).
Robert Harvey

20

Bien sûr pas de problème:

foreach(object item in sequence)
{
    if (item == null) continue;
    foreach(PropertyInfo property in item.GetType().GetProperties())
    {
        // do something with the property
    }
}

pourquoi avez-vous ajouté cette ligne? if (item == null) continue;Personnellement, je pense que si vous avez un objet nul à ce stade, quelque chose s'est mal passé beaucoup plus tôt et c'est là que la validation devrait être, ou ai-je tort?
Rafael Herscovici

@Dementic: Bien sûr, nous pourrions dire que la séquence doit contenir des références non nulles.
Eric Lippert

3

Utilisez Reflection pour ce faire

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

2

J'ai cherché la réponse à une question similaire sur cette page, j'ai écrit les réponses à plusieurs questions similaires qui peuvent aider les personnes qui entrent sur cette page.

Liste des classes

La classe List <T> représente la liste des objets accessibles par index. Il appartient à l'espace de noms System.Collection.Generic. La classe List peut être utilisée pour créer une collection de différents types comme des entiers, des chaînes, etc. La classe List fournit également les méthodes pour rechercher, trier et manipuler des listes.

Classe avec propriété :

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

OR, Classe avec champ :

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, Liste des objets (sans les mêmes cellules):

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, Liste des objets (il doit avoir les mêmes cellules):

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, Liste des objets (avec clé):

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, Liste des dictionnaires

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

Bonne chance..


0

Un petit mot d'avertissement, si "faire des choses" signifie mettre à jour la valeur de la propriété réelle que vous visitez ET s'il existe une propriété de type struct le long du chemin de l'objet racine à la propriété visitée, la modification que vous avez apportée à la propriété sera ne pas être reflété sur l'objet racine.


0

Je n'ai pu obtenir aucune des méthodes ci-dessus, mais cela a fonctionné. Le nom d'utilisateur et le mot de passe pour DirectoryEntry sont facultatifs.

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

0

Une solution de copier-coller (méthodes d'extension) principalement basée sur les réponses précédentes à cette question.

Gère également correctement IDicitonary (ExpandoObject / dynamic) qui est souvent nécessaire pour traiter ces éléments reflétés.

Non recommandé pour une utilisation dans les boucles serrées et autres chemins chauds. Dans ces cas, vous aurez besoin d'une compilation d'arborescence de mise en cache / émission IL / expression.

    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());
    }
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.