c # foreach (властивість в об'єкті) ... Чи існує простий спосіб зробити це?


92

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

Створюючи деякі модульні тести для своїх класів, я вирішив, що хочу прокрутити кожен об'єкт у списку, а потім прокрутити кожну властивість цього об'єкта ...

Я думав зробити це буде так просто, як ...

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

Але це не спрацювало! :( Я отримую цю помилку ...

"оператор foreach не може працювати із змінними типу 'Application.Object', оскільки 'Application.Object' не містить загальнодоступного визначення для 'GetEnumerator'"

Хто-небудь знає про спосіб зробити це без тонн ifs і петлі або не вдаючись до чогось занадто складного?


5
У майбутньому, будь ласка, не кажіть "Це не працює" у питанні. Натомість вкажіть проблему (помилка компілятора тощо). Дякую!
Роберт Харві,

2
Оновлено! Дякую за голови Роберт
Jammerz858

Відповіді:


143

Спробуйте:

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

Також зверніть увагу, що Type.GetProperties()є перевантаження, яке приймає набір прапорів прив'язки, щоб ви могли відфільтрувати властивості за різними критеріями, такими як рівень доступності, див. MSDN для більш докладної інформації: Метод Type.GetProperties (BindingFlags) Нарешті, але не менш важливо, не забудьте додати посилання на збірку "system.Reflection".

Наприклад, щоб вирішити всі загальнодоступні властивості:

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

Будь ласка, дайте мені знати, чи це працює належним чином.


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 Це чудовий момент щодо властивостей лише для запису! Я майже забув про них. Мій код вийде з ладу, якщо він зустріне властивість з nullgetter, але я впевнений, що OP зрозуміє, як отримати лише ті властивості, які йому потрібні.
Сергій Калініченко

27

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

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

Звідти ви можете отримати доступ таким чином :

property.Name
property.GetValue(obj, null)

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


1
Я когось засмутив? Я відкритий для того, щоб знати, що в цьому поганого, інакше я ніколи не дізнаюся.
Грант Томас

Ви, ймовірно, отримали голос проти, коли ваш код помилився (до редагування вашого ніндзя).
Роберт Харві,

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;особисто я думаю, якщо у вас на цей момент був нульовий об'єкт, щось пішло не так набагато раніше, і саме тут має бути перевірка, чи я помиляюся?
Рафаель Герсковічі

@Dementic: Звичайно, ми могли б сказати, що послідовність повинна містити ненульові посилання.
Ерік Ліпперт

3

Для цього використовуйте Reflection

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

2

Я шукав відповідь на подібне питання на цій сторінці, я писав відповіді на кілька подібних питань, які можуть допомогти людям, які заходять на цю сторінку.

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

Клас List <T> представляє список об'єктів, до яких можна отримати доступ за допомогою індексу. Він підпадає під простір імен System.Collection.Generic. Клас списку може бути використаний для створення колекції різних типів, таких як цілі числа, рядки тощо. Клас списку також надає методи пошуку, сортування та керування списками.

Клас з майном :

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

Удачі..


0

Невелике слово обережності, якщо "зробити щось" означає оновлення значення фактичного властивості, яке ви відвідуєте, І якщо на шляху від кореневого об'єкта до відвіданого властивості є властивість типу struct, зміна, яку ви внесли у властивість не відображаються на кореневому об'єкті.


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;
    }
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.