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당신이 유형의 객체를 반환한다면 Invoiceinv.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.