C #에서 리플렉션을 사용하여 중첩 된 개체의 속성 가져 오기


82

다음 객체가 주어지면 :

public class Customer {
    public String Name { get; set; }
    public String Address { get; set; }
}

public class Invoice {
    public String ID { get; set; }
    public DateTime Date { get; set; }
    public Customer BillTo { get; set; }
}

나는 통과하는 반사를 사용하고 싶습니다 Invoice얻을 Name의 속성을 Customer. 이 코드가 작동한다고 가정하면 다음과 같습니다.

Invoice inv = GetDesiredInvoice();  // magic method to get an invoice
PropertyInfo info = inv.GetType().GetProperty("BillTo.Address");
Object val = info.GetValue(inv, null);

물론 이것은 "BillTo.Address"가 Invoice클래스 의 유효한 속성이 아니기 때문에 실패합니다 .

그래서 저는 그 기간에 문자열을 조각으로 나누는 방법을 작성하고 내가 관심이있는 최종 값을 찾기 위해 객체를 걷는 방법을 시도했습니다. 그것은 잘 작동하지만 완전히 편안하지는 않습니다.

public Object GetPropValue(String name, Object obj) {
    foreach (String part in name.Split('.')) {
        if (obj == null) { return null; }

        Type type = obj.GetType();
        PropertyInfo info = type.GetProperty(part);
        if (info == null) { return null; }

        obj = info.GetValue(obj, null);
    }
    return obj;
}

이 방법을 개선하는 방법 또는이 문제를 해결하는 더 나은 방법에 대한 아이디어가 있습니까?

게시 후 편집 , 몇 가지 관련 게시물을 보았습니다 ... 그러나이 질문을 구체적으로 다루는 답변은없는 것 같습니다. 또한 내 구현에 대한 피드백을 받고 싶습니다.


그냥 궁금해, GetDesiredInvoice당신이 유형의 객체를 반환한다면 Invoice왜 inv.BillTo.Name직접 사용하지 않습니까?
— ram

나는 실제로 이것을 약간 다르게 사용하고 있습니다. 나는 개체를 가져다가 인쇄용 템플릿과 병합하는 프로세서에 전달합니다.
— jheddings 2009

그것은 단지 약간의 "무력한 힘"을 느꼈고 더 나은 방법이 있어야하는 것처럼 보였다. 그러나 지금까지의 답변으로 볼 때 내가 완전히 기지를 벗어난 것은 아닌 것 같습니다.
— jheddings 2009

3
내가이 일에 미쳤다고 생각했지만 누군가 나와 같은 문제를 겪은 것 같습니다.
— 그건

또한이를 확장 방법으로 사용하기위한 다른 주제에 대한 답장을 확인하십시오. stackoverflow.com/questions/1196991/…
— jheddings

답변:


12

나는 실제로 당신의 논리가 괜찮다고 생각합니다. 개인적으로 나는 아마도 그것을 변경하여 첫 번째 매개 변수로 개체를 전달합니다 (PropertyInfo.GetValue와 더 인라인이므로 덜 놀랍습니다).

또한 속성 스택을 검색한다는 것을 분명히하기 위해 GetNestedPropertyValue와 같은 이름으로도 호출 할 수 있습니다.


매개 변수 재정렬 및 ​​제안 된 이름 변경에 대한 좋은 요청입니다.
— itowlson 2009

의견을 보내 주셔서 감사합니다. 두 가지 제안을 모두 구현했습니다. 결국 Object클래스 의 확장 메서드로 전환 하여 매개 변수 재정렬에 대한 요점을 강화했습니다.
— jheddings 2009

왜 받아 들여지는 대답입니까? OP가 요청한 것처럼 중첩 된 개체의 속성을 얻는 데 도움이되지 않습니다.
— Levitikon

@Levitikon OP의 두 번째 코드 세트는 요청 된 작업을 수행하는 적절한 방법을 보여줍니다. 그게 내 요점이었다. 답변 자체에 게시 된 코드에는 아무런 문제가 없습니다.
— Reed Copsey

27

다음과 같은 방법을 사용하여 (중첩 된 클래스) 속성에서 값을 가져옵니다.

"특성"

"주소. 거리"

"주소. 국가. 이름"

    public static object GetPropertyValue(object src, string propName)
    {
        if (src == null) throw new ArgumentException("Value cannot be null.", "src");
        if (propName == null) throw new ArgumentException("Value cannot be null.", "propName");

        if(propName.Contains("."))//complex type nested
        {
            var temp = propName.Split(new char[] { '.' }, 2);
            return GetPropertyValue(GetPropertyValue(src, temp[0]), temp[1]);
        }
        else
        {
            var prop = src.GetType().GetProperty(propName);
            return prop != null ? prop.GetValue(src, null) : null;
        }
    }

다음은 바이올린입니다 : https://dotnetfiddle.net/PvKRH0


속성이 null이면 작동하지 않으므로 작업하기 전에 처음에 src가 null인지 확인해야합니다.
— Furtiro 2017-06-12

@Furtiro 예, src (또는 propName)가 null인지 여부는 작동하지 않습니다. throw 예외를 추가했습니다. 감사합니다
— DevT 2017-06-12

기꺼이 도와주세요! 그러나 트리플 중첩 속성으로 작동하지 않습니다. 2, 슬프지만 훌륭한 코드 후에 중지됩니다!
— Furtiro 2017-06-12

이상한 @Furtiro, 방금 게시물에 추가 한 바이올린에서 볼 수 있듯이 작동해야합니다. 문제를 찾을 수 있습니다.
— DevT 2017-06-12

@DevT 안녕하세요, 중첩 된 클래스를 사용할 때 GetProperty가 작동하도록 만드는 동일한 접근 방식을 사용 하시겠습니까? 즉 var property = type.GetProperty (sortProperty); 중첩 클래스로 실패하면 (널 결과가 나오기 때문에) 솔루션이 이에 답할 수 있다고 생각합니다. (자세한 내용은 여기에 제공된 솔루션 stackoverflow.com/questions/11336713/… 이 중첩 된 클래스로 실패 함)
— Kynao

13

나는 파티에 조금 늦게 해요 알고, 다른 사람들이 말했듯이, 구현 괜찮습니다
... 간단한 사용 사례에 대해 .
그러나 저는 그 사용 사례를 정확히 해결하는 라이브러리 인 Pather.CSharp를 개발했습니다 . Nuget 패키지
로도 제공됩니다 .

주요 클래스는 Resolver그와 Resolve방법.
당신은 그에게 전달할 객체와 속성 경로를 , 그리고 그것은 반환 원하는 값을 .

Invoice inv = GetDesiredInvoice();  // magic method to get an invoice
var resolver = new Resolver();
object result = resolver.Resolve(inv, "BillTo.Address");

그러나 배열 및 사전 액세스를 포함하여 더 복잡한 속성 경로를 해결할 수도 있습니다.
예를 들어 주소Customer 가 여러 개인 경우

public class Customer {
    public String Name { get; set; }
    public IEnumerable<String> Addresses { get; set; }
}

을 사용하여 두 번째에 액세스 할 수 있습니다 Addresses[1].

Invoice inv = GetDesiredInvoice();  // magic method to get an invoice
var resolver = new Resolver();
object result = resolver.Resolve(inv, "BillTo.Addresses[1]");

2
중첩 된 속성에 대한 null 개체, 즉 NullObject가 인보이스에서 null 인 NullObject.Id를 어떻게 처리합니까?
— AVFC_Bubble88

10

리플렉션을 사용해야하는 ACTUAL 개체에 액세스해야합니다. 제가 의미하는 바는 다음과 같습니다.

대신 :

Invoice inv = GetDesiredInvoice();  // magic method to get an invoice
PropertyInfo info = inv.GetType().GetProperty("BillTo.Address");
Object val = info.GetValue(inv, null);

다음을 수행하십시오 (댓글에 따라 편집 됨).

Invoice inv = GetDesiredInvoice();  // magic method to get an invoice
PropertyInfo info = inv.GetType().GetProperty("BillTo");
Customer cust = (Customer)info.GetValue(inv, null);

PropertyInfo info2 = cust.GetType().GetProperty("Address");
Object val = info2.GetValue(cust, null);

자세한 내용은이 게시물을 참조하십시오. 리플렉션을 사용하여 객체 속성의 속성 설정


대답 해 주셔서 감사합니다. 첫 번째 수준 속성의 값을 얻는 방법을 알고 있지만 중첩 된 속성을 얻는 방법이 궁금합니다. 실제 응용 프로그램에서는 실제 개체에 액세스 할 수 없습니다.
— jheddings 2009

1
중첩 된 속성을 직접 가져와야합니다. 또한 "Invoice"가 T이고 "Property.Property.Property"경로의 문자열이있는 상황에 있습니다. 각 속성을 만지작 거리면 안됩니다.
— Levitikon

이것은 나를 위해 해냈습니다. BillTo.Address에도 운이 없었습니다. 속성 설정이 같은 방식으로 작동하는지 궁금합니다.
— Yusif_Nurizade

7

파티에 너무 늦게 들리지 않기를 바라면서 내 솔루션을 추가하고 싶습니다.이 상황에서 재귀를 사용하십시오.

public static Object GetPropValue(String name, object obj, Type type)
    {
        var parts = name.Split('.').ToList();
        var currentPart = parts[0];
        PropertyInfo info = type.GetProperty(currentPart);
        if (info == null) { return null; }
        if (name.IndexOf(".") > -1)
        {
            parts.Remove(currentPart);
            return GetPropValue(String.Join(".", parts), info.GetValue(obj, null), info.PropertyType);
        } else
        {
            return info.GetValue(obj, null).ToString();
        }
    }

6

당신은 당신의 "불편 함"의 근원을 설명하지 않지만, 당신의 코드는 기본적으로 나에게 소리가 나는 것처럼 보인다.

내가 질문 할 유일한 것은 오류 처리입니다. 코드가 null 참조를 통과하려고 시도하거나 속성 이름이 존재하지 않는 경우 null을 반환합니다. 이렇게하면 오류가 숨겨집니다. BillTo 고객이 없기 때문에 null을 반환했는지 또는 "BilTo.Address"철자가 틀 렸기 때문에 또는 BillTo 고객이 있고 해당 주소가 null이기 때문에 null을 반환했는지 알기 어렵습니다! 이 경우 메서드가 충돌하고 태워 지도록했습니다. 예외를 탈출 시키거나 더 친근한 것으로 래핑하면됩니다.


3

여기 에 열거자인 경우 중첩 된 속성 을 건너 뛰고 더 깊게 계속하는 또 다른 구현이 있습니다. 문자열 유형의 속성은 열거 검사의 영향을받지 않습니다.

public static class ReflectionMethods
{
    public static bool IsNonStringEnumerable(this PropertyInfo pi)
    {
        return pi != null && pi.PropertyType.IsNonStringEnumerable();
    }

    public static bool IsNonStringEnumerable(this object instance)
    {
        return instance != null && instance.GetType().IsNonStringEnumerable();
    }

    public static bool IsNonStringEnumerable(this Type type)
    {
        if (type == null || type == typeof(string))
            return false;
        return typeof(IEnumerable).IsAssignableFrom(type);
    }

    public static Object GetPropValue(String name, Object obj)
    {
        foreach (String part in name.Split('.'))
        {
            if (obj == null) { return null; }
            if (obj.IsNonStringEnumerable())
            {
                var toEnumerable = (IEnumerable)obj;
                var iterator = toEnumerable.GetEnumerator();
                if (!iterator.MoveNext())
                {
                    return null;
                }
                obj = iterator.Current;
            }
            Type type = obj.GetType();
            PropertyInfo info = type.GetProperty(part);
            if (info == null) { return null; }

            obj = info.GetValue(obj, null);
        }
        return obj;
    }
}

이 질문과

PropertyInfo가 Berryl 의 컬렉션인지 확인하는 방법

MVC 프로젝트에서 이것을 사용하여 예제 별로 정렬 할 속성을 전달하여 데이터를 동적으로 정렬합니다 .

result = result.OrderBy((s) =>
                {
                    return ReflectionMethods.GetPropValue("BookingItems.EventId", s);
                }).ToList();

여기서 BookingItems는 객체 목록입니다.


2
> Get Nest properties e.g., Developer.Project.Name
private static System.Reflection.PropertyInfo GetProperty(object t, string PropertName)
            {
                if (t.GetType().GetProperties().Count(p => p.Name == PropertName.Split('.')[0]) == 0)
                    throw new ArgumentNullException(string.Format("Property {0}, is not exists in object {1}", PropertName, t.ToString()));
                if (PropertName.Split('.').Length == 1)
                    return t.GetType().GetProperty(PropertName);
                else
                    return GetProperty(t.GetType().GetProperty(PropertName.Split('.')[0]).GetValue(t, null), PropertName.Split('.')[1]);
            }

1
   if (info == null) { /* throw exception instead*/ } 

존재하지 않는 속성을 요청하면 실제로 예외가 발생합니다. 코딩 한 방식에 따라 GetPropValue를 호출하고 null을 반환하면 속성이 존재하지 않았는지 또는 속성이 존재하지만 값이 null인지는 알 수 없습니다.


또한 obj가 null인지 확인을 루프 외부로 이동하십시오.
— Kevin Brock

죄송합니다. obj가 반복적으로 사용되는 것을 보지 못했습니다. 매개 변수를 변경하는 것은 좋은 프로그래밍 관행이 아닙니다. 이로 인해 향후 혼란이 발생할 수 있습니다. obj 매개 변수에 다른 변수를 사용하여 루프 내에서 순회하십시오.
— Kevin Brock

Kevin : 다른 변수를 사용하려면 끝에 obj에 할당하거나 메서드를 재귀 적으로 만들어야합니다. 개인적으로, 나는 (좋은 코멘트는 ... 좋은 것이지만)이 문제라고 생각하지 않습니다
— 리드 Copsey

1
@Levitikon OP는 "이 방법을 개선하는 방법에 대한 아이디어가 있거나이 문제를 해결하는 더 나은 방법이 있습니까?"라고 말했습니다. 따라서 OP가 개선을 요청했기 때문에 이것은 답변이 아니라 의견입니다.
— AaronLS

1
    public static string GetObjectPropertyValue(object obj, string propertyName)
    {
        bool propertyHasDot = propertyName.IndexOf(".") > -1;
        string firstPartBeforeDot;
        string nextParts = "";

        if (!propertyHasDot)
            firstPartBeforeDot = propertyName.ToLower();
        else
        {
            firstPartBeforeDot = propertyName.Substring(0, propertyName.IndexOf(".")).ToLower();
            nextParts = propertyName.Substring(propertyName.IndexOf(".") + 1);
        }

        foreach (var property in obj.GetType().GetProperties())
            if (property.Name.ToLower() == firstPartBeforeDot)
                if (!propertyHasDot)
                    if (property.GetValue(obj, null) != null)
                        return property.GetValue(obj, null).ToString();
                    else
                        return DefaultValue(property.GetValue(obj, null), propertyName).ToString();
                else
                    return GetObjectPropertyValue(property.GetValue(obj, null), nextParts);
        throw new Exception("Property '" + propertyName.ToString() + "' not found in object '" + obj.ToString() + "'");
    }

1
귀하의 소송의 근거가 무엇인지, OP가 좋은 관행이되는 데 어떻게 도움이되는지 설명하십시오.
— DontVoteMeDown 2013-06-24

1

너무 늦었지만 내 솔루션을 공유하고 싶었습니다. 이 솔루션은 주로 중첩 된 속성이 있는지 확인하는 것입니다. 그러나 필요한 경우 속성 값을 반환하도록 쉽게 조정할 수 있습니다.

private static PropertyInfo _GetPropertyInfo(Type type, string propertyName)
        {
            //***
            //*** Check if the property name is a complex nested type
            //***
            if (propertyName.Contains("."))
            {
                //***
                //*** Get the first property name of the complex type
                //***
                var tempPropertyName = propertyName.Split(".", 2);
                //***
                //*** Check if the property exists in the type
                //***
                var prop = _GetPropertyInfo(type, tempPropertyName[0]);
                if (prop != null)
                {
                    //***
                    //*** Drill down to check if the nested property exists in the complex type
                    //***
                    return _GetPropertyInfo(prop.PropertyType, tempPropertyName[1]);
                }
                else
                {
                    return null;
                }
            }
            else
            {
                return type.GetProperty(propertyName, BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance);
            }
        }

이 솔루션을 찾기 위해 몇 가지 게시물을 참조해야했습니다. 여러 중첩 속성 유형에 대해 작동한다고 생각합니다.


0

동일한 문제를 해결해야 할 때 인터넷 연결이 끊어져서 '바퀴를 다시 발명'해야했습니다.

static object GetPropertyValue(Object fromObject, string propertyName)
{
    Type objectType = fromObject.GetType();
    PropertyInfo propInfo = objectType.GetProperty(propertyName);
    if (propInfo == null && propertyName.Contains('.'))
    {
        string firstProp = propertyName.Substring(0, propertyName.IndexOf('.'));
        propInfo = objectType.GetProperty(firstProp);
        if (propInfo == null)//property name is invalid
        {
            throw new ArgumentException(String.Format("Property {0} is not a valid property of {1}.", firstProp, fromObject.GetType().ToString()));
        }
        return GetPropertyValue(propInfo.GetValue(fromObject, null), propertyName.Substring(propertyName.IndexOf('.') + 1));
    }
    else
    {
        return propInfo.GetValue(fromObject, null);
    }
}

이것은 모든 것이 속성 인 한 중첩 범위에 관계없이 속성 이름에 사용하는 모든 문자열의 문제를 해결합니다.


-7

시험 inv.GetType().GetProperty("BillTo+Address");

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