c # foreach (개체의 속성)…이 작업을 수행하는 간단한 방법이 있습니까?


92

여러 속성을 포함하는 클래스가 있습니다 (차이가 있으면 모두 문자열입니다).
또한 클래스의 여러 인스턴스가 포함 된 목록이 있습니다.

클래스에 대한 단위 테스트를 만드는 동안 목록의 각 개체를 반복 한 다음 해당 개체의 각 속성을 반복하기로 결정했습니다.

이렇게하면 간단하다고 생각했는데 ...

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

그러나 이것은 작동하지 않았습니다! :(이 오류가 발생합니다 ...

" 'Application.Object'에 'GetEnumerator'에 대한 공개 정의가 포함되어 있지 않기 때문에 foreach 문은 'Application.Object'유형의 변수에서 작동 할 수 없습니다."

많은 ifs 및 루프없이 또는 너무 복잡한 작업을 수행하지 않고이를 수행하는 방법을 아는 사람이 있습니까?


5
앞으로는 질문에 "작동하지 않습니다"라고 말하지 마십시오. 대신 발생한 문제 (컴파일러 오류 등)를 지정하십시오. 감사!
Robert Harvey

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()반환 하므로 두 번째 매개 변수 로 전달하는 것이 안전하다는 것을 알기 위해 호출 하기 전에 추가 필터가 필요합니다 .GetValuenull

쓰기 전용 속성과 액세스 할 수없는 속성을 제거하려면 필터를 추가로 수정해야 할 수 있습니다.


실제로 사용할 수있는 속성으로 만 작업을 수행하는 경우 +1-쓰기 전용 속성을 필터링 할 수도 있습니다.

@hvd 쓰기 전용 속성에 대한 훌륭한 포인트입니다! 나는 그들에 대해 거의 잊었습니다. nullgetter를 사용 하여 속성을 발견하면 내 코드가 충돌 하지만 OP가 필요한 속성 만 가져 오는 방법을 알아낼 것이라고 확신합니다.
Sergey Kalinichenko

27

거의 다 왔으니, 컬렉션이나 속성 모음의 형태로 속성에 액세스 할 수있을 것으로 기대하는 대신 유형에서 속성을 가져 오면됩니다.

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

거기에서 다음과 같이 액세스 할 수 있습니다 .

property.Name
property.GetValue(obj, null)

GetValue문자열을 문자의 집합이기 때문에, 당신은 또한 요구가있을 경우 문자를 반환하는 인덱스를 지정할 수 있습니다 - 두 번째 매개 변수는 속성 컬렉션을 반환 작동합니다 인덱스 값을 지정할 수 있습니다.


1
내가 누군가를 화나게 했습니까? 나는 이것에 대해 무엇이 잘못되었는지 알기 위해 열려있다. 그렇지 않으면 나는 결코 배울 수 없을 것이다.
그랜트 토마스

코드가 잘못되었을 때 (닌자 편집 전) 비추천을 받았을 것입니다.
Robert Harvey

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;개인적으로 그 시점에서 null 객체가 있으면 훨씬 일찍 문제가 발생했으며 여기에서 유효성 검사가 있어야합니까, 아니면 내가 틀렸습니까?
Rafael Herscovici

@Dementic : 물론, 시퀀스가 ​​널이 아닌 참조를 포함해야한다고 말할 수 있습니다.
Eric Lippert

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

OR, Class with field :

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, 개체 목록 (동일한 셀 없음) :

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

OR, 개체 목록 (동일한 셀이 있어야 함) :

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, 객체 목록 (키 포함) :

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

OR, 사전 목록

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

약간의주의가 필요합니다. "일부 작업 수행"은 방문하는 실제 속성의 값을 업데이트하는 것을 의미하고 루트 개체에서 방문한 속성으로의 경로를 따라 구조체 유형 속성이있는 경우 속성에서 변경 한 내용이 루트 개체에 반영되지 않습니다.


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;
    }
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.