c # foreach (propiedad en el objeto)… ¿Existe una forma sencilla de hacer esto?

92

Tengo una clase que contiene varias propiedades (todas son cadenas si hay alguna diferencia).
También tengo una lista, que contiene muchas instancias diferentes de la clase.

Mientras creaba algunas pruebas unitarias para mis clases, decidí que quería recorrer cada objeto en la lista y luego recorrer cada propiedad de ese objeto ...

Pensé que hacer esto sería tan simple como ...

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

¡Pero esto no funcionó! :( Recibo este error ...

"La instrucción foreach no puede operar en variables de tipo 'Application.Object' porque 'Application.Object' no contiene una definición pública para 'GetEnumerator'"

¿Alguien conoce una forma de hacer esto sin toneladas de ifs y bucles o sin meterse en nada demasiado complejo?

Jammerz858
fuente
5
En el futuro, no diga "No funciona" en una pregunta. En su lugar, especifique el problema que tiene (error del compilador, etc.). ¡Gracias!
Robert Harvey
2
¡Actualizado! Gracias por el
aviso

Respuestas:

143

Prueba esto:

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

También tenga en cuenta que Type.GetProperties()tiene una sobrecarga que acepta un conjunto de indicadores de vinculación para que pueda filtrar propiedades en un criterio diferente como el nivel de accesibilidad, consulte MSDN para obtener más detalles: Método Type.GetProperties (BindingFlags) Por último, pero no menos importante, no olvide agregue la referencia de ensamblaje "system.Reflection".

Por ejemplo, para resolver todas las propiedades públicas:

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

Por favor, avíseme si esto funciona como se esperaba.

sll
fuente
4
Una vez que tenga un control sobre las propiedades de los objetos, ¿hay alguna forma de tomar el valor de cada propiedad? es decir, un nombre o código postal, por ejemplo
JsonStatham
36

Puede recorrer todas las propiedades no indexadas de un 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));
}

Dado que GetProperties()devuelve indexadores , así como propiedades simples, necesita un filtro adicional antes de llamar GetValuepara saber que es seguro pasar nullcomo segundo parámetro.

Es posible que deba modificar más el filtro para eliminar las propiedades de solo escritura y otras inaccesibles.

Sergey Kalinichenko
fuente
+1 por hacer solo algo con las propiedades que realmente se pueden usar; es posible que también desee filtrar las propiedades de solo escritura.
@hvd ¡Es un excelente punto sobre las propiedades de solo escritura! Casi me olvido de ellos. Mi código fallará si encuentra una propiedad con nullgetter, pero estoy seguro de que OP descubrirá cómo obtener solo las propiedades que necesita.
Sergey Kalinichenko
27

Ya casi está allí, solo necesita obtener las propiedades del tipo, en lugar de esperar que las propiedades sean accesibles en forma de colección o bolsa de propiedades:

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

Desde allí puedes acceder así :

property.Name
property.GetValue(obj, null)

Con GetValueel segundo parámetro le permitirá especificar valores de índice, que funcionarán con propiedades que devuelvan colecciones; dado que una cadena es una colección de caracteres, también puede especificar un índice para devolver un carácter si es necesario.

Grant Thomas
fuente
1
¿He molestado a alguien? Estoy abierto a saber qué hay de malo en esto, de lo contrario, es posible que nunca lo aprenda.
Grant Thomas
Probablemente obtuviste el voto negativo cuando tu código estaba mal (antes de tu edición ninja).
Robert Harvey
20

Claro, no hay problema:

foreach(object item in sequence)
{
    if (item == null) continue;
    foreach(PropertyInfo property in item.GetType().GetProperties())
    {
        // do something with the property
    }
}
Eric Lippert
fuente
¿Por qué agregaste esta línea? if (item == null) continue;personalmente, creo que si tienes un objeto nulo en ese momento, algo salió mal mucho antes y ahí es donde debería estar la validación, ¿o estoy equivocado?
Rafael Herscovici
@Dementic: Claro, podríamos decir que la secuencia debe contener referencias no nulas.
Eric Lippert
3

Usa Reflection para hacer esto

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

Busqué la respuesta a una pregunta similar en esta página, escribí las respuestas a varias preguntas similares que pueden ayudar a las personas que ingresan a esta página.

Lista de clase

La clase List <T> representa la lista de objetos a los que se puede acceder por índice. Viene bajo el espacio de nombres System.Collection.Generic. La clase List se puede usar para crear una colección de diferentes tipos como enteros, cadenas, etc. La clase List también proporciona los métodos para buscar, ordenar y manipular listas.

Clase con propiedad :

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

O, Clase con 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);
    }

}

O, Lista de objetos (sin las mismas celdas):

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

O, Lista de objetos (debe tener las mismas celdas):

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

O, Lista de objetos (con clave):

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

O, lista de diccionario

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

Buena suerte..

mirzaei.sajad
fuente
0

Una pequeña advertencia, si "hacer algunas cosas" significa actualizar el valor de la propiedad real que visita Y si hay una propiedad de tipo de estructura a lo largo de la ruta desde el objeto raíz a la propiedad visitada, el cambio que realizó en la propiedad será no reflejarse en el objeto raíz.

Dogu Arslan
fuente
0

Una solución de copiar y pegar (métodos de extensión) basada principalmente en respuestas anteriores a esta pregunta.

También maneja correctamente IDicitonary (ExpandoObject / dynamic) que a menudo se necesita cuando se trata de este material reflejado.

No se recomienda su uso en bucles estrechos y otros caminos calientes. En esos casos, necesitará alguna compilación de árbol de expresión / emisión de IL / caché.

    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
fuente
-1

No pude conseguir que ninguna de las formas anteriores funcionara, pero funcionó. El nombre de usuario y la contraseña de DirectoryEntry son opcionales.

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