プロパティがExpandoObjectに存在するかどうかを検出するにはどうすればよいですか?


188

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

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

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


5
@CodeInChaos:表示されたコードはの値をチェックしないことに注意してくださいdata.myProperty。何typeof data.myPropertyが戻るかをチェックします。data.myProperty存在する可能性があり、に設定されているのは正しいですundefinedが、その場合、typeof以外の何かが返されます"undefined"。したがって、このコードは機能します。
Aasmund Eldhuset 2012年

回答:


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をオーバーロードして常にtrueを返すようにし、プロパティが定義されていない場合は戻り値を「未定義」に設定しました。if(someObject.someParam!= "undefined")...そして、動作します:)
Softlion

:)も可能ですが、オーバーロードされたのではなく、「オーバーライドされた」という意味だと思います。
Dykam

うん。「未定義」を、別の場所で作成した特別なオブジェクトのconst値に再度変更しました。キャストの問題を
回避し

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とは異なり、ExpandoObjectは、存在しないプロパティでnullをチェックするとスローされます。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を実装しているだけDDictですが、宣言されていないプロパティのnullチェックに対してスローしない動的オブジェクト(クラス)がGithubにあります。

https://github.com/b9chris/GracefulDynamicDictionary

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


なぜリフレクションを使用しないのに、DynamicDictionaryに反対票を投じたのですか?
Softlion、2014年

これは同じソリューションなので、投票できます:)
Softlion

3
それは確かに同じ解決策ではありません。
Chris Moschini 2014年

「プロパティが見つからない場合にスローしない同様のサブクラスを作成する必要があります。」=>そうです!ああ、そうではありません。私の解決策は優れています。それはスローします-私たちはそれを望んでいるので、そしてTryXXが使用されている場合はスローすることもできません。
Softlion、2014年

1
これは、これがまさに私がここにいる理由であり、いくつかのコード(ビューバッグ)が壊れていない理由を理解できませんでした。ありがとうございました。
Adam Tolley、2014年

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
例外の原因がわかっている場合は、そのタイプも知っている必要があるので、(WhateverException)をキャッチしてください。そうしないと、たとえばOutOfMemoryExceptionなどの予期しない例外が発生した場合でも、コードが黙って続行されます。
marklam 2013年

4
任意のゲッターをに渡すことができIsPropertyExistます。この例では、をスローできることがわかりますInvalidOperationException。実際には、どの例外がスローされるかはわかりません。貨物カルトに対抗するために+1。
ピエダー2013

2
このソリューションは、パフォーマンスが重要である場合は受け入れられません。たとえば、500回以上の反復を伴うループで使用すると、合計して何秒もの遅延が発生する可能性があります。例外がキャッチされるたびに、スタックを例外オブジェクトにコピーする必要があります
Jim109

1
Re:パフォーマンス:接続されているデバッガーとConsole.WriteLineは遅いビットです。ここでの10,000回の反復は200ミリ秒未満です(反復ごとに2つの例外)。例外のない同じテストは、数ミリ秒かかります。つまり、このコードの使用でプロパティが欠落することはまれであると予想される場合、または限られた回数だけ呼び出す場合、または結果をキャッシュできる場合は、すべてがその場所にあり、過度なものはないことに注意してください。ここで規制された警告が重要です。
カオス

10

拡張メソッドを作成して、次のようなことをしたかったのです。

dynamic myDynamicObject;
myDynamicObject.propertyName = "value";

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

...しかしExpandoObject、C#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の拡張機能に関する有用なコメントとリンクに賛成票を投じてください。
ロベルト

1

タイププロパティのセットを取得するためにリフレクションを使用したくないのはなぜですか?このような

 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
 } 

それが動的に追加されたプロパティを返すかどうかはわかりませんが、Dynamicオブジェクトのメソッドを返すと思います。
Dykam、

1

この拡張メソッドは、プロパティの存在を確認してから、値またはnullを返します。これは、アプリケーションで不要な例外をスローしたくない場合に役立ちます。少なくとも1つは役立ちます。

    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がundefinedと同じであると見なせる場合は、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
これは、動的オブジェクトでプロパティが存在することを確認する方法ではなく、動的オブジェクトを実装する方法を示しています。
マットウォーレン

問題のプロパティに対してnullチェックを実行することで、動的インスタンスにプロパティがあるかどうかを確認できます。
ctorx 2011

2
「これは動的オブジェクトを実装する方法を示しています」:はい、実際にそうです。この質問の解決策は、実装に依存するため、一般的な解決策はありません。
Softlion、2011年

@Softlionいいえ、解決策は私たち使用を中止しなければならないことです
nik.shornikov

@Softlion Tryxxxメソッドのポイントは何ですか?プロパティが見つからない場合、TryGetはfalseを返しません。そのため、結果を確認する必要があります。返品は無意味です。TrySetでは、キーが存在しない場合、falseを返すのではなく、例外をスローします。なぜこれを回答として使用するのか理解できません。「この質問の解決策は、実装に依存しているため、一般的な解決策はありません」というコメントに自分で書き込んだ場合、それもまた当てはまりません。実際の解決策については、Dykamの答えを見てください。
pqsk 2014年

-5

これを試してください

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

3
これは、実装の詳細である動的名の下に隠されたオブジェクトのプロパティの存在をチェックします。投稿する前に実際のコードでソリューションを検証しましたか?まったく機能しないはずです。
Softlion、2012

このコードをリアルタイムシナリオで使用しました。それはうまくいきます。
ベンカット2013年

6
プロパティが存在する場合でも、常にnullを与えます。
アトランティス2013年

基本オブジェクトでは機能しますが、ExpandoObjectsでは機能しません。ダイナミクス、わかりません。
Joe

1
確認のため、これdynamicオブジェクトでも機能しません(常にを返しますnull)。
コーディングを行って2015年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.