web-dev-qa-db-ja.com

ExpandoObjectにプロパティが存在するかどうかを検出する方法は?

JavaScriptでは、undefinedキーワードを使用してプロパティが定義されているかどうかを検出できます。

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

動的キーワードをExpandoObjectとともに使用して、例外をスローせずにC#でこれを行うにはどうすればよいですか?

177
Softlion

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

ここで重要な区別をする必要があります。

ここでの回答のほとんどは、質問で言及されている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とは異なり、ExpandoObjectは、存在しないプロパティでnullをチェックするとスローします。 dynamicオブジェクトから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のディクショナリ実装です。

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を実装しているだけです-しかし、Githubで宣言されていないプロパティのnullチェックをスローしない動的オブジェクトです:

https://github.com/b9chris/GracefulDynamicDictionary

NuGetを介してプロジェクトに追加するだけの場合、その名前は GracefulDynamicDictionary です。

27
Chris Moschini

最近、非常によく似た質問に答えました: 動的オブジェクトのメンバーをどのように反映しますか?

まもなく、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
10
Alexander G

拡張メソッドを作成したかったので、次のようなことができました:

dynamic myDynamicObject;
myDynamicObject.propertyName = "value";

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

...しかし、C#5のドキュメントによれば、ExpandoObjectに拡張機能を作成することはできません(詳細 here )。

そこで、クラスヘルパーを作成しました。

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

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
 } 
1
Vokinneberg

この拡張メソッドは、プロパティの存在を確認し、値または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
realdanielbyrne