c # foreach (свойство в объекте)… Есть ли простой способ сделать это?

92

У меня есть класс, содержащий несколько свойств (все строки, если это имеет значение).
У меня также есть список, который содержит много разных экземпляров класса.

Создавая несколько модульных тестов для своих классов, я решил, что хочу перебрать каждый объект в списке, а затем перебрать каждое свойство этого объекта ...

Я думал сделать это так же просто, как ...

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

Но это не сработало! :( У меня такая ошибка ...

«Оператор foreach не может работать с переменными типа Application.Object, потому что Application.Object не содержит общедоступного определения для GetEnumerator»

Кто-нибудь знает, как это сделать без тонны if и циклов или не вдаваясь во что-то слишком сложное?

Jammerz858
источник
5
В будущем, пожалуйста, не говорите «Не работает» в вопросе. Вместо этого укажите проблему, с которой вы столкнулись (ошибка компилятора и т. Д.). Благодарность!
Роберт Харви
2
Обновлено! Спасибо за внимание, Роберт
Jammerz858

Ответы:

143

Попробуйте:

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

Также обратите внимание, что у Type.GetProperties()него есть перегрузка, которая принимает набор флагов привязки, поэтому вы можете отфильтровать свойства по другим критериям, таким как уровень доступности, см. MSDN для получения дополнительных сведений: Type.GetProperties Method (BindingFlags) И последнее, но не менее важное, не забудьте добавьте ссылку на сборку "system.Reflection".

Например, чтобы разрешить все общедоступные свойства:

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

Пожалуйста, дайте мне знать, работает ли это должным образом.

sll
источник
4
Если у вас есть дескриптор свойств объекта, есть ли способ получить значение каждого свойства? например, имя или почтовый индекс
JsonStatham
36

Вы можете просмотреть все неиндексированные свойства объекта следующим образом:

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

Так как GetProperties()возвращает индексаторы, а также простые свойства, вам понадобится дополнительный фильтр перед вызовом, GetValueчтобы знать, что можно безопасно передавать nullв качестве второго параметра.

Возможно, вам придется дополнительно изменить фильтр, чтобы исключить свойства, доступные только для записи или недоступные по другим причинам.

Сергей Калиниченко
источник
+1 за то, что вы делаете что-то только со свойствами, которые действительно могут быть использованы - вы также можете отфильтровать свойства, доступные только для записи.
@hvd Это отличный отзыв о свойствах только для записи! Я почти забыл о них. Мой код выйдет из строя, если встретит свойство с nullполучателем, но я уверен, что OP выяснит, как получить только те свойства, которые ему нужны.
Сергей Калиниченко
27

Вы почти готовы, вам просто нужно получить свойства из типа, а не ожидать, что свойства будут доступны в форме коллекции или пакета свойств:

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

Оттуда вы можете получить доступ так :

property.Name
property.GetValue(obj, null)

С GetValueпомощью второго параметра позволит задавать значение индекса, которые будут работать со свойствами возвращения коллекции - поскольку строка представляет собой набор символов, вы можете также указать индекс для возврата символа , если потребности быть.

Грант Томас
источник
1
Я кого-то обидел? Я открыт для понимания, что в этом плохого, иначе я никогда не узнаю.
Grant Thomas
Вы, вероятно, получили голос против, когда ваш код был неправильным (до редактирования ниндзя).
Роберт Харви
20

Конечно, нет проблем:

foreach(object item in sequence)
{
    if (item == null) continue;
    foreach(PropertyInfo property in item.GetType().GetProperties())
    {
        // do something with the property
    }
}
Эрик Липперт
источник
зачем вы добавили эту строчку? if (item == null) continue;Лично я думаю, что если в этот момент у вас есть нулевой объект, что-то пошло не так намного раньше, и именно здесь должна быть проверка, или я ошибаюсь?
Рафаэль Херсковичи, 01
@Dementic: Конечно, мы могли бы сказать, что последовательность должна содержать ненулевые ссылки.
Эрик Липперт,
3

Используйте для этого Reflection

SomeClass A = SomeClass(...)
PropertyInfo[] properties = A.GetType().GetProperties();
Арион
источник
2

Я искал ответ на аналогичный вопрос на этой странице, я написал ответы на несколько похожих вопросов, которые могут помочь людям, заходящим на эту страницу.

Список классов

Класс List <T> представляет список объектов, к которым можно получить доступ по индексу. Он входит в пространство имен System.Collection.Generic. Класс List может использоваться для создания коллекции различных типов, таких как целые числа, строки и т. Д. Класс List также предоставляет методы для поиска, сортировки и управления списками.

Класс с имуществом :

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

ИЛИ, класс с полем :

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

}

ИЛИ, Список объектов (без одинаковых ячеек):

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

ИЛИ, Список объектов (в нем должны быть одинаковые ячейки):

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

ИЛИ, Список объектов (с ключом):

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

ИЛИ, Список словарей

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

Удачи..

mirzaei.sajad
источник
0

Небольшое предостережение, если «сделать что-нибудь» означает обновление значения фактического свойства, которое вы посещаете, И если есть свойство типа структуры на пути от корневого объекта к посещенному свойству, изменение, которое вы сделали в свойстве, будет не отражаться на корневом объекте.

Догу Арслан
источник
0

Решение для копирования и вставки (методы расширения) в основном основано на более ранних ответах на этот вопрос.

Также правильно обрабатывает IDicitonary (ExpandoObject / dynamic), который часто требуется при работе с этим отраженным материалом.

Не рекомендуется использовать в узких петлях и других горячих дорожках. В этих случаях вам понадобится некоторая компиляция дерева кеширования / испускания IL / выражений.

    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());
    }
Зар Шардан
источник
-1

Я не мог заставить работать ни один из вышеперечисленных способов, но это сработало. Имя пользователя и пароль для DirectoryEntry необязательны.

   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
источник