ExpandoObject에 속성이 있는지 감지하는 방법은 무엇입니까?


188

자바 스크립트에서는 undefined 키워드를 사용하여 속성이 정의되어 있는지 확인할 수 있습니다.

if( typeof data.myProperty == "undefined" ) ...

C # ExpandoObject에서 예외를 던지거나하지 않고 dynamic 키워드를 사용하여 어떻게합니까?


5
@CodeInChaos : 표시된 코드는 data.myProperty; 의 값을 확인하지 않습니다 . 어떤 typeof data.myProperty결과를 반환 하는지 확인 합니다. 그것은 그 정확 data.myProperty존재하고 설정할 수 undefined있지만,이 경우, typeof이외를 반환합니다 "undefined". 따라서이 코드는 작동합니다.
Aasmund Eldhuset

답변:


181

MSDN 에 따르면 선언은 IDictionary를 구현하고 있음을 보여줍니다.

public sealed class ExpandoObject : IDynamicMetaObjectProvider, 
    IDictionary<string, Object>, ICollection<KeyValuePair<string, Object>>, 
    IEnumerable<KeyValuePair<string, Object>>, IEnumerable, INotifyPropertyChanged

이를 사용하여 멤버가 정의되어 있는지 확인할 수 있습니다.

var expandoObject = ...;
if(((IDictionary<String, object>)expandoObject).ContainsKey("SomeMember")) {
    // expandoObject.SomeMember exists.
}

3
이 검사를 간단하게하기 위해 TryGetValue를 오버로드하고 속성이 정의되지 않은 경우 반환 값을 "undefined"로 설정하여 항상 true를 반환합니다. if (someObject.someParam! = "undefined") ... 그리고 작동합니다 :)
Softlion

또한 가능합니다 :),하지만 당신은 오버로드 대신 "재정의"를 의미한다고 생각합니다.
다이 캄

네. "undefined"를 다른 곳에서 만든 특수 객체의 const 값으로 다시 변경했습니다. 캐스팅 문제를 방지합니다 : p
Softlion

1
이 솔루션은 여전히 ​​최신 상태라고 생각합니다. 리플렉션 가격에 대해 아무 말도하지 마십시오. 직접 테스트하고 여유가 있는지 확인하십시오
nik.shornikov

1
@ BlueRaja-DannyPflughoeft 그렇습니다. 캐스트가 없으면 내부에서 얻을 수있는 경우와 같이 동적 호출이 될 것입니다. 더 구체적으로, 그것은 명시 적으로 구현됩니다 : github.com/mono/mono/blob/master/mcs/class/dlr/Runtime/…
Dykam

28

여기서 중요한 구분이 필요합니다.

여기에있는 대부분의 답변은 질문에 언급 된 ExpandoObject에만 해당됩니다. 그러나 ASP.Net MVC ViewBag를 사용하는 경우 일반적인 사용법 (및 검색시이 질문에 해당하는 이유)이 있습니다. 이는 DynamicObject의 사용자 정의 구현 / 하위 클래스이므로 임의의 속성 이름을 null로 확인할 때 예외가 발생하지 않습니다. 다음과 같은 속성을 선언 할 수 있다고 가정하십시오.

@{
    ViewBag.EnableThinger = true;
}

그런 다음 값을 확인하고 설정되어 있는지 여부를 확인하려고한다고 가정하십시오. 다음은 유효하고 컴파일되며 예외가 발생하지 않으며 올바른 답변을 제공합니다.

if (ViewBag.EnableThinger != null && ViewBag.EnableThinger)
{
    // Do some stuff when EnableThinger is true
}

이제 EnableThinger 선언을 제거하십시오. 동일한 코드가 컴파일되고 올바르게 실행됩니다. 반사 할 필요가 없습니다.

ViewBag와 달리 존재하지 않는 속성에서 null을 확인하면 ExpandoObject가 발생합니다. MVC ViewBag의 부드러운 기능을 dynamic객체에서 벗어나게 하려면 던져지지 않는 동적 구현을 ​​사용해야합니다.

MVC ViewBag에서 정확한 구현을 간단히 사용할 수 있습니다.

. . .
public override bool TryGetMember(GetMemberBinder binder, out object result)
{
    result = ViewData[binder.Name];
    // since ViewDataDictionary always returns a result even if the key does not exist, always return true
    return true;
}
. . .

https://github.com/ASP-NET-MVC/aspnetwebstack/blob/master/src/System.Web.Mvc/DynamicViewDataDictionary.cs

MVC ViewPage에서 MVC 뷰에 묶여있는 것을 볼 수 있습니다.

http://aspnetwebstack.codeplex.com/SourceControl/latest#src/System.Web.Mvc/ViewPage.cs

DynamicViewDataDictionary의 올바른 동작의 핵심은 ViewDataDictionary의 Dictionary 구현입니다.

public object this[string key]
{
    get
    {
        object value;
        _innerDictionary.TryGetValue(key, out value);
        return value;
    }
    set { _innerDictionary[key] = value; }
}

https://github.com/ASP-NET-MVC/aspnetwebstack/blob/master/src/System.Web.Mvc/ViewDataDictionary.cs

다시 말해서, 그것은 그것이 무엇이든 관계없이 항상 모든 키에 대한 값을 반환합니다-아무것도 없을 때 단순히 null을 반환합니다. 그러나 ViewDataDictionary는 MVC의 모델에 연결해야하는 부담이 있으므로 MVC 뷰 외부에서 사용하기 위해 우아한 사전 부분 만 제거하는 것이 좋습니다.

여기에 모든 내장을 실제로 게시하기에는 너무 길다. 대부분 IDictionary를 구현하고 있지만 DDictGithub에서 선언되지 않은 속성에 대한 null 검사를 던지지 않는 동적 객체 (class )가 있습니다.

https://github.com/b9chris/GracefulDynamicDictionary

NuGet을 통해 프로젝트에 추가하려는 경우 이름은 GracefulDynamicDictionary 입니다.


왜 리플렉션을 사용하지 않는 DynamicDictionary에 투표를 했습니까?
Softlion

다음과 같은 솔루션이므로 투표 할 수 있습니다 :)
Softlion

3
가장 확실한 해결책은 아닙니다.
Chris Moschini 2016 년

"속성을 찾을 수 없을 때 발생하지 않는 유사한 서브 클래스를 작성해야합니다." =>입니다! 아뇨. 내 솔루션이 더 좋습니다. 던지기-우리가 원하기 때문에 TryXX를 사용하면 던질 수도 없습니다.
Softlion

1
이것, 이것은 내가 왜 여기에 있는지, 왜 일부 코드 (viewbag)가 깨지지 않았는지 알 수 없었습니다. 감사합니다.
Adam Tolley

11

최근에 비슷한 질문에 대답했습니다. 동적 객체의 멤버를 어떻게 반영합니까?

곧 ExpandoObject가 유일한 동적 객체는 아닙니다. 리플렉션은 정적 유형 (IDynamicMetaObjectProvider를 구현하지 않는 유형)에서 작동합니다. 이 인터페이스를 구현하는 유형의 경우 리플렉션은 기본적으로 쓸모가 없습니다. ExpandoObject의 경우 기본 사전에서 속성이 키로 정의되어 있는지 간단히 확인할 수 있습니다. 다른 구현에서는 어려울 수 있으며 때로는 유일한 방법은 예외로 작업하는 것입니다. 자세한 내용은 위의 링크를 참조하십시오.


11

업데이트 됨 : 대리자를 사용하고 동적 개체 속성이있는 경우 값을 가져 오려고 시도 할 수 있습니다. 속성이 없으면 단순히 예외를 잡아서 false를 반환합니다.

한번보세요, 그것은 나를 위해 잘 작동합니다 :

class Program
{
    static void Main(string[] args)
    {
        dynamic userDynamic = new JsonUser();

        Console.WriteLine(IsPropertyExist(() => userDynamic.first_name));
        Console.WriteLine(IsPropertyExist(() => userDynamic.address));
        Console.WriteLine(IsPropertyExist(() => userDynamic.last_name));
    }

    class JsonUser
    {
        public string first_name { get; set; }
        public string address
        {
            get
            {
                throw new InvalidOperationException("Cannot read property value");
            }
        }
    }

    static bool IsPropertyExist(GetValueDelegate getValueMethod)
    {
        try
        {
            //we're not interesting in the return value. What we need to know is whether an exception occurred or not
            getValueMethod();
            return true;
        }
        catch (RuntimeBinderException)
        {
            // RuntimeBinderException occurred during accessing the property
            // and it means there is no such property         
            return false;
        }
        catch
        {
            //property exists, but an exception occurred during getting of a value
            return true;
        }
    }

    delegate string GetValueDelegate();
}

코드 출력은 다음과 같습니다.

True
True
False

2
@marklam 예외의 원인을 모르는 경우 모든 예외를 포착하는 것은 좋지 않습니다. 우리는 필드가 없을 것으로 예상하기 때문에 괜찮습니다.
Alexander G

3
예외의 원인을 알고 있으면 해당 유형도 알아야합니다. catch (WhateverException) 그렇지 않으면 예기치 않은 예외 (예 : OutOfMemoryException)가 있어도 코드가 자동으로 수행됩니다.
marklam

4
모든 getter를에 전달할 수 있습니다 IsPropertyExist. 이 예제에서는을 던질 수 있음을 알고 있습니다 InvalidOperationException. 실제로 어떤 예외가 발생할 수 있는지 전혀 모릅니다. 화물 숭배를 막기 위해 +1.
piedar

2
성능이 중요한 경우이 솔루션은 수용 할 수 없습니다. 예를 들어 500 회 이상의 반복이있는 루프에서 사용될 경우 합산되어 수초의 지연이 발생할 수 있습니다. 예외가 발견 될 때마다 스택을 예외 객체에 복사해야합니다
Jim109

1
Re : Performance : 연결된 디버거와 Console.WriteLine이 느린 비트입니다. 여기에서 10,000 회 반복은 200ms 미만이 소요됩니다 (반복 당 2 회 예외). 예외없이 동일한 테스트는 몇 밀리 초가 걸립니다. 즉,이 코드를 사용 하여 속성이 거의 누락 되지 않을 것으로 예상 되거나 제한된 횟수로 호출하거나 결과를 캐시 할 수있는 경우 모든 것이 제자리에 있고 그중 하나도 없다는 것을 인식하십시오 -여기서 경고가 중요합니다.
혼돈

10

확장 메소드 를 작성하여 다음과 같은 작업을 수행 하려고했습니다 .

dynamic myDynamicObject;
myDynamicObject.propertyName = "value";

if (myDynamicObject.HasProperty("propertyName"))
{
    //...
}

...하지만 ExpandoObjectC # 5 설명서 에 따라 확장 프로그램을 만들 수 없습니다 (자세한 내용은 여기 ).

그래서 나는 클래스 도우미를 만들었습니다.

public static class ExpandoObjectHelper
{
    public static bool HasProperty(ExpandoObject obj, string propertyName)
    {
        return ((IDictionary<String, object>)obj).ContainsKey(propertyName);
    }
}

그것을 사용하려면 :

// If the 'MyProperty' property exists...
if (ExpandoObjectHelper.HasProperty(obj, "MyProperty"))
{
    ...
}

4
유용한 의견에 대한 투표 및 ExpandoObject 확장에 대한 링크.
Roberto

1

유형이 적절한 예를 얻기 위해 Reflection을 사용하고 싶지 않은 이유는 무엇입니까? 이렇게

 dynamic v = new Foo();
 Type t = v.GetType();
 System.Reflection.PropertyInfo[] pInfo =  t.GetProperties();
 if (Array.Find<System.Reflection.PropertyInfo>(pInfo, p => { return p.Name == "PropName"; }).    GetValue(v,  null) != null))
 {
     //PropName initialized
 } 

동적으로 추가 된 속성을 반환할지 확실하지 않습니다. 동적 객체의 메서드를 반환한다고 생각합니다.
Dykam

1

이 확장 메서드는 속성이 있는지 확인한 다음 값 또는 null을 반환합니다. 이 기능은 응용 프로그램에서 불필요한 예외를 발생시키지 않도록하는 데 도움이됩니다.

    public static object Value(this ExpandoObject expando, string name)
    {
        var expandoDic = (IDictionary<string, object>)expando;
        return expandoDic.ContainsKey(name) ? expandoDic[name] : null;
    }

다음과 같이 사용할 수있는 경우 :

  // lookup is type 'ExpandoObject'
  object value = lookup.Value("MyProperty");

또는 지역 변수가 '동적'인 경우 먼저 ExpandoObject로 캐스트해야합니다.

  // lookup is type 'dynamic'
  object value = ((ExpandoObject)lookup).Value("PropertyBeingTested");

1

사용 사례에 따라 null이 정의되지 않은 것과 동일한 것으로 간주 될 수있는 경우 ExpandoObject를 DynamicJsonObject로 전환 할 수 있습니다.

    dynamic x = new System.Web.Helpers.DynamicJsonObject(new ExpandoObject());
    x.a = 1;
    x.b = 2.50;
    Console.WriteLine("a is " + (x.a ?? "undefined"));
    Console.WriteLine("b is " + (x.b ?? "undefined"));
    Console.WriteLine("c is " + (x.c ?? "undefined"));

산출:

a is 1
b is 2.5
c is undefined


-3

이봐, 많은 CPU 사이클을 소비하는 모든 것에 대해 Reflection 사용을 중단하십시오.

해결책은 다음과 같습니다.

public class DynamicDictionary : DynamicObject
{
    Dictionary<string, object> dictionary = new Dictionary<string, object>();

    public int Count
    {
        get
        {
            return dictionary.Count;
        }
    }

    public override bool TryGetMember(GetMemberBinder binder, out object result)
    {
        string name = binder.Name;

        if (!dictionary.TryGetValue(binder.Name, out result))
            result = "undefined";

        return true;
    }

    public override bool TrySetMember(SetMemberBinder binder, object value)
    {
        dictionary[binder.Name] = value;
        return true;
    }
}

4
동적 객체에서 속성이 종료되는 것을 보는 방법이 아니라 동적 객체를 구현하는 방법을 보여줍니다.
Matt Warren

해당 속성에 대해 null 검사를 수행하여 동적 인스턴스에 속성이 있는지 확인할 수 있습니다.
ctorx

2
"이것은 동적 객체를 구현하는 방법을 보여줍니다": 예, 사실입니다. 이 질문에 대한 해결책은 다음과 같습니다. 구현에 따라 일반적인 솔루션이 없습니다.
Softlion

@Softlion 아니요, 해결책은 우리 가 사용 을 중단 해야하는 것입니다
nik.shornikov

@Softlion Tryxxx 방법의 요점은 무엇입니까? TryGet은 속성을 찾지 못하면 false를 반환하지 않으므로 결과를 여전히 확인해야합니다. 반환은 쓸모가 없습니다. TrySet에서 키가 없으면 false를 반환하는 대신 예외가 발생합니다. "이 질문에 대한 해결책은 다음과 같습니다. 구현에 의존하는 일반적인 솔루션이 없습니다"라는 의견에 여기에 썼다면 왜 이것을 대답으로 사용하는지 이해하지 못합니다. 실제 솔루션에 대한 Dykam의 답변을보십시오.
pqsk

-5

이거 한번 해봐

public bool PropertyExist(object obj, string propertyName)
{
 return obj.GetType().GetProperty(propertyName) != null;
}

3
이것은 동적 이름 아래에 숨겨진 객체의 속성이 있는지 확인합니다. 이는 구현 세부 사항입니다. 게시하기 전에 실제 코드로 솔루션을 확인 했습니까? 전혀 작동하지 않아야합니다.
Softlion

실시간 시나리오에서이 코드를 사용했습니다. 잘 작동합니다.
Venkat

6
속성이 존재하더라도 항상 null을 제공합니다.
atlantis 2016 년

기본 객체에는 작동하지만 ExpandoObject에는 작동하지 않습니다. 역학, 확실하지 않습니다.
Joe

1
확인하기 위해 이것은 객체에서도 작동 하지 않습니다dynamic (항상 리턴 null).
Gone Coding
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.