c # foreach (właściwość w obiekcie)… Czy jest na to prosty sposób?

92

Mam klasę zawierającą kilka właściwości (wszystkie są ciągami znaków, jeśli ma to znaczenie).
Mam też listę, która zawiera wiele różnych instancji tej klasy.

Tworząc testy jednostkowe dla moich klas, zdecydowałem, że chcę przejść przez każdy obiekt na liście, a następnie przejść przez każdą właściwość tego obiektu ...

Pomyślałem, że zrobienie tego byłoby tak proste, jak ...

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

Ale to nie zadziałało! :( Otrzymuję ten błąd ...

„Instrukcja foreach nie może działać na zmiennych typu 'Application.Object', ponieważ 'Application.Object' nie zawiera publicznej definicji dla 'GetEnumerator'”

Czy ktoś zna sposób na zrobienie tego bez mnóstwa ifów i pętli lub bez wchodzenia w nic zbyt skomplikowanego?

Jammerz858
źródło
5
W przyszłości nie mów „To nie działa” w pytaniu. Zamiast tego określ występujący problem (błąd kompilatora itp.). Dzięki!
Robert Harvey,
2
Zaktualizowano! Dzięki za heads up Robert
Jammerz858

Odpowiedzi:

143

Spróbuj:

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

Należy również pamiętać, że Type.GetProperties()ma przeciążenie, które akceptuje zestaw flag powiązań, dzięki czemu można odfiltrować właściwości według różnych kryteriów, takich jak poziom dostępności, więcej szczegółów można znaleźć w witrynie MSDN: Metoda Type.GetProperties (BindingFlags) Wreszcie, nie zapomnij o dodaj odwołanie do zestawu „system.Reflection”.

Na przykład, aby rozwiązać wszystkie właściwości publiczne:

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

Daj mi znać, czy to działa zgodnie z oczekiwaniami.

sll
źródło
4
Gdy już masz już uchwyt na właściwości obiektów, czy w ogóle istnieje możliwość pobrania wartości każdej właściwości? czyli na przykład imię i nazwisko lub kod pocztowy
JsonStatham
36

Możesz przeglądać wszystkie nieindeksowane właściwości obiektu w następujący sposób:

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

Ponieważ GetProperties()zwraca indeksatory, a także proste właściwości, przed wywołaniem potrzebny jest dodatkowy filtr, GetValueaby wiedzieć, że można bezpiecznie przekazać nulljako drugi parametr.

Konieczne może być dalsze zmodyfikowanie filtru w celu wyeliminowania właściwości tylko do zapisu i niedostępnych w inny sposób.

Sergey Kalinichenko
źródło
+1 za zrobienie czegoś tylko z właściwościami, które faktycznie mogą być użyte - możesz także chcieć odfiltrować właściwości tylko do zapisu.
@hvd To doskonała uwaga na temat właściwości tylko do zapisu! Prawie o nich zapomniałem. Mój kod ulegnie awarii, jeśli napotka właściwość z nullfunkcją getter, ale jestem pewien, że OP dowie się, jak uzyskać tylko te właściwości, których potrzebuje.
Sergey Kalinichenko
27

Jesteś już prawie na miejscu, wystarczy pobrać właściwości z typu, zamiast oczekiwać, że właściwości będą dostępne w formie kolekcji lub worka z nieruchomościami:

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

Stamtąd możesz uzyskać dostęp w następujący sposób :

property.Name
property.GetValue(obj, null)

Z GetValuedrugiego parametru pozwoli na określenie wartości indeksu, które będą pracować z właściwości powrocie kolekcji - ponieważ ciąg jest zbiorem znaków, można również określić indeks zwrócić charakter, jeśli musi być.

Grant Thomas
źródło
1
Czy kogoś zdenerwowałem? Jestem otwarty na to, by wiedzieć, co w tym złego, inaczej mógłbym się nigdy nie nauczyć.
Grant Thomas
Prawdopodobnie otrzymałeś głos przeciw, gdy kod był nieprawidłowy (przed edycją ninja).
Robert Harvey,
20

Pewnie nie ma problemu:

foreach(object item in sequence)
{
    if (item == null) continue;
    foreach(PropertyInfo property in item.GetType().GetProperties())
    {
        // do something with the property
    }
}
Eric Lippert
źródło
dlaczego dodałeś tę linię? if (item == null) continue;Osobiście myślę, że jeśli w tym momencie otrzymałeś obiekt zerowy, coś poszło nie tak dużo wcześniej i właśnie tam powinno być sprawdzanie poprawności, czy się mylę?
Rafael Herscovici
@Dementic: Jasne, możemy powiedzieć, że sekwencja musi zawierać odwołania niezerowe.
Eric Lippert
3

Użyj do tego odbicia

SomeClass A = SomeClass(...)
PropertyInfo[] properties = A.GetType().GetProperties();
Arion
źródło
2

Szukałem odpowiedzi na podobne pytanie na tej stronie, napisałem odpowiedzi na kilka podobnych pytań, które mogą pomóc osobom wchodzącym na tę stronę.

Lista klas

Klasa List <T> reprezentuje listę obiektów, do których można uzyskać dostęp za pomocą indeksu. Znajduje się w przestrzeni nazw System.Collection.Generic. Klasy List można używać do tworzenia kolekcji różnych typów, takich jak liczby całkowite, łańcuchy itp. Klasa List udostępnia również metody wyszukiwania, sortowania i manipulowania listami.

Klasa z właściwością :

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, klasa z polem :

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

}

OR, lista obiektów (bez tych samych komórek):

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

LUB, Lista obiektów (musi mieć te same komórki):

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

OR, Lista obiektów (z kluczem):

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

LUB, Lista słowników

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

Powodzenia..

mirzaei.sajad
źródło
0

Małe słowo przestrogi, jeśli „zrób coś” oznacza aktualizację wartości rzeczywistej właściwości, którą odwiedzasz ORAZ jeśli na ścieżce od obiektu głównego do odwiedzanej właściwości znajduje się właściwość typu struct, zmiana dokonana w tej właściwości nie zostaną odzwierciedlone w obiekcie głównym.

Dogu Arslan
źródło
0

Rozwiązanie typu „kopiuj-wklej” (metody rozszerzające) oparte głównie na wcześniejszych odpowiedziach na to pytanie.

Prawidłowo obsługuje również IDicitonary (ExpandoObject / dynamic), co jest często potrzebne w przypadku tych odzwierciedlonych rzeczy.

Niezalecane do użytku w ciasnych pętlach i innych gorących ścieżkach. W takich przypadkach będziesz potrzebować trochę kompilacji pamięci podręcznej / IL emitowania / wyrażenia.

    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
źródło
-1

Nie mogłem znaleźć żadnego z powyższych sposobów, ale to zadziałało. Nazwa użytkownika i hasło dla DirectoryEntry są opcjonalne.

   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
źródło