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

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

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

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



답변

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.
}


답변

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

여기에있는 대부분의 답변은 질문에 언급 된 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 입니다.


답변

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

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


답변

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


답변

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

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"))
{
    ...
}


답변

유형이 적절한 예를 얻기 위해 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
 } 


답변

이 확장 메서드는 속성이 있는지 확인한 다음 값 또는 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");