プロパティが動的変数で利用可能かどうかをテストする


225

私の状況は非常に単純です。私のコードのどこかに私はこれを持っています:

dynamic myVariable = GetDataThatLooksVerySimilarButNotTheSame();

//How to do this?
if (myVariable.MyProperty.Exists)   
//Do stuff

したがって、基本的に私の質問は、動的変数で特定のプロパティが使用可能であることを(例外をスローせずに)確認する方法です。できますGetType()が、オブジェクトのタイプを実際に知る必要がないので、それは避けたいです。私が本当に知りたいのは、プロパティ(またはメソッド、それが人生を楽にするなら)が利用可能かどうかです。ポインタはありますか?


1
ここにいくつかの提案があります: stackoverflow.com/questions/2985161/…-しかし、これまでのところ受け入れられた答えはありません。
Andrew Anderson

私が思っていたカントー私は逃しmは何があるかどうおかげで、私は、ソリューションのモミ1を作る方法を見ることができます
roundcrisis

回答:


159

dynamicC#コンパイラで動的バインディングが処理される方法を再実装しない限り、変数にアクセスしようとせずに特定のメンバーがあるかどうかを確認する方法はないと思います。C#の仕様によると、これは実装で定義されているため、おそらく多くの推測が含まれます。

したがって、実際にメンバーにアクセスして、例外が発生した場合は例外をキャッチする必要があります。

dynamic myVariable = GetDataThatLooksVerySimilarButNotTheSame();

try
{
    var x = myVariable.MyProperty;
    // do stuff with x
}
catch (RuntimeBinderException)
{
    //  MyProperty doesn't exist
} 

2
長い間、これを答えとしてマークして
おき

8
より優れたソリューション-stackoverflow.com/questions/2839598/…–
ministrymason

20
@ministrymasonキャストして操作する場合、それはでIDictionaryのみExpandoObject機能し、他のdynamicオブジェクトでは機能しません。
スビック

5
RuntimeBinderExceptionMicrosoft.CSharp.RuntimeBinder名前空間にあります。
DavidRR

8
このシナリオの詳細に関係なく、if / elseの代わりにtry / catchを使用するのは一般的に悪い習慣です。
Alexander Ryan Baggett 2017年

74

マルティンの答えスヴィックの答えを比較してみようと思いました...

次のプログラムは、次の結果を返します。

Testing with exception: 2430985 ticks
Testing with reflection: 155570 ticks

void Main()
{
    var random = new Random(Environment.TickCount);

    dynamic test = new Test();

    var sw = new Stopwatch();

    sw.Start();

    for (int i = 0; i < 100000; i++)
    {
        TestWithException(test, FlipCoin(random));
    }

    sw.Stop();

    Console.WriteLine("Testing with exception: " + sw.ElapsedTicks.ToString() + " ticks");

    sw.Restart();

    for (int i = 0; i < 100000; i++)
    {
        TestWithReflection(test, FlipCoin(random));
    }

    sw.Stop();

    Console.WriteLine("Testing with reflection: " + sw.ElapsedTicks.ToString() + " ticks");
}

class Test
{
    public bool Exists { get { return true; } }
}

bool FlipCoin(Random random)
{
    return random.Next(2) == 0;
}

bool TestWithException(dynamic d, bool useExisting)
{
    try
    {
        bool result = useExisting ? d.Exists : d.DoesntExist;
        return true;
    }
    catch (Exception)
    {
        return false;
    }
}

bool TestWithReflection(dynamic d, bool useExisting)
{
    Type type = d.GetType();

    return type.GetProperties().Any(p => p.Name.Equals(useExisting ? "Exists" : "DoesntExist"));
}

結果として、リフレクションを使用することをお勧めします。下記参照。


blandのコメントへの返答:

比率はreflection:exception100000回の反復の目盛りです。

Fails 1/1: - 1:43 ticks
Fails 1/2: - 1:22 ticks
Fails 1/3: - 1:14 ticks
Fails 1/5: - 1:9 ticks
Fails 1/7: - 1:7 ticks
Fails 1/13: - 1:4 ticks
Fails 1/17: - 1:3 ticks
Fails 1/23: - 1:2 ticks
...
Fails 1/43: - 1:2 ticks
Fails 1/47: - 1:1 ticks

...十分に公正-1/47未満の確率で失敗すると予想される場合は、例外を検討してください。


上記は、GetProperties()毎回実行していることを前提としています。GetProperties()各タイプの結果を辞書などにキャッシュすることで、プロセスを高速化できる場合があります。これは、同じタイプのセットを何度もチェックする場合に役立ちます。


7
私は同意し、必要に応じて自分の作品への反映を好みます。Try / Catchに対する利点は、例外がスローされた場合のみです。では、ここでリフレクションを使用する前に誰かが尋ねるべきことは-それは特定の方法である可能性が高いですか?90%または75%の時間で、コードは合格しますか?その後、Try / Catchは依然として最適です。それが空中にある場合、または選択肢が多すぎて最も可能性が高い場合、あなたの反射はその場にあります。
2013年

@bland編集済み回答。
dav_i 2013年

1
おかげで、本当に完全に見えます。
2013年

@dav_i両方の動作が異なるため、両方を比較するのは公平ではありません。スヴィックの答えはより完全です。
nawfal 2014

1
@dav_iいいえ、同じ機能は実行しません。Martijnの回答は、動的に宣言されたC#の通常のコンパイル時型にプロパティが存在するかどうかをチェックします(つまり、コンパイル時の安全性チェックを無視します)。一方、svickの答えは、プロパティが本当に動的なオブジェクト、つまりを実装するものに存在するかどうかをチェックします。私はあなたの答えの背後にある動機を理解し、感謝しています。そう答えるのは公正です。IIDynamicMetaObjectProvider
nawfal 14

52

多分リフレクションを使用しますか?

dynamic myVar = GetDataThatLooksVerySimilarButNotTheSame();
Type typeOfDynamic = myVar.GetType();
bool exist = typeOfDynamic.GetProperties().Where(p => p.Name.Equals("PropertyName")).Any(); 

2
「GetType()は実行できるが、それは避けたい」という質問からの引用
roundcrisis


12
次のものなしで実行できますWhere.Any(p => p.Name.Equals("PropertyName"))
dav_i


3
ワンライナーとして:((Type)myVar.GetType()).GetProperties().Any(x => x.Name.Equals("PropertyName"))。タイプへのキャストは、コンパイラーをラムダについて満足させるために必要です。
MushinNoShin 2015

38

誰かを助けるために:

メソッドGetDataThatLooksVerySimilarButNotTheSame()がを返す場合は、確認前にExpandoObjectキャストすることもできますIDictionary

dynamic test = new System.Dynamic.ExpandoObject();
test.foo = "bar";

if (((IDictionary<string, object>)test).ContainsKey("foo"))
{
    Console.WriteLine(test.foo);
}

3
なぜこの回答がより多くの票を持たないのかわからない。なぜなら、それは要求されたとおりに実行するからです(例外のスローやリフレクションはありません)。
Wolfshead 2016年

7
@Wolfsheadこの回答は、動的オブジェクトがExpandoObjectまたはIDictionary <string、object>を実装する何かであることがわかっている場合に最適ですが、それ以外の場合は失敗します。
Damian Powell

9

これに対する2つの一般的なソリューションには、呼び出しの作成とのキャッチ、RuntimeBinderExceptionリフレクションを使用した呼び出しの確認、またはテキスト形式へのシリアル化とそこからの解析が含まれます。例外の問題は、構築時に現在のコールスタックがシリアル化されるため、速度が非常に遅いことです。JSONまたは類似のものにシリアル化すると、同様のペナルティが発生します。これはリフレクションを残しますが、基になるオブジェクトが実際にその上に実際のメンバーを持つPOCOである場合にのみ機能します。辞書、COMオブジェクト、または外部Webサービスの動的ラッパーの場合、リフレクションは役に立ちません。

別の解決策はDynamicMetaObject、DLRがメンバー名を見るときにメンバー名を取得することです。以下の例では、静的クラス(Dynamic)を使用してAgeフィールドをテストし、表示しています。

class Program
{
    static void Main()
    {
        dynamic x = new ExpandoObject();

        x.Name = "Damian Powell";
        x.Age = "21 (probably)";

        if (Dynamic.HasMember(x, "Age"))
        {
            Console.WriteLine("Age={0}", x.Age);
        }
    }
}

public static class Dynamic
{
    public static bool HasMember(object dynObj, string memberName)
    {
        return GetMemberNames(dynObj).Contains(memberName);
    }

    public static IEnumerable<string> GetMemberNames(object dynObj)
    {
        var metaObjProvider = dynObj as IDynamicMetaObjectProvider;

        if (null == metaObjProvider) throw new InvalidOperationException(
            "The supplied object must be a dynamic object " +
            "(i.e. it must implement IDynamicMetaObjectProvider)"
        );

        var metaObj = metaObjProvider.GetMetaObject(
            Expression.Constant(metaObjProvider)
        );

        var memberNames = metaObj.GetDynamicMemberNames();

        return memberNames;
    }
}

Dynamiteynugetパッケージはすでにこれを行っていることがわかります。(nuget.org/packages/Dynamitey
ダミアン・パウエル

8

デニスの答えは、JsonObjectsを使用する別のソリューションを考えさせられました。

ヘッダープロパティチェッカー:

Predicate<object> hasHeader = jsonObject =>
                                 ((JObject)jsonObject).OfType<JProperty>()
                                     .Any(prop => prop.Name == "header");

または多分もっと良い:

Predicate<object> hasHeader = jsonObject =>
                                 ((JObject)jsonObject).Property("header") != null;

例えば:

dynamic json = JsonConvert.DeserializeObject(data);
string header = hasHeader(json) ? json.header : null;

1
この答えのどこが間違っているかを知る機会はありますか?
Charles HETIER、2015

なぜこれが否決されたのかわからない、私にとってはうまくいった。各プロパティのPredicateをヘルパークラスに移動し、Invokeメソッドを呼び出して各プロパティからブール値を返しました。
markp3rry 2015

7

まあ、私は同様の問題に直面しましたが、単体テストでした。

SharpTestsExを使用すると、プロパティが存在するかどうかを確認できます。JSONオブジェクトは動的であるため、誰かが名前を変更したり、JavaScriptなどで変更を忘れたりする可能性があるため、これをコントローラーのテストに使用します。コントローラーを作成するときにすべてのプロパティをテストすると、安全性が向上します。

例:

dynamic testedObject = new ExpandoObject();
testedObject.MyName = "I am a testing object";

ここで、SharTestsExを使用します。

Executing.This(delegate {var unused = testedObject.MyName; }).Should().NotThrow();
Executing.This(delegate {var unused = testedObject.NotExistingProperty; }).Should().Throw();

これを使用して、「Should()。NotThrow()」を使用してすべての既存のプロパティをテストします。

それはおそらく話題外ですが、誰かにとっては役に立つかもしれません。


ありがとう、とても便利です。:SharpTestsEx Iを使用すると、動的プロパティの値をテストするには、次の行を使用する((string)(testedObject.MyName)).Should().Be("I am a testing object");
Remkoヤンセン

2

@karaskの回答に続いて、次のようにヘルパーとして関数をラップできます。

public static bool HasProperty(ExpandoObject expandoObj,
                               string name)
{
    return ((IDictionary<string, object>)expandoObj).ContainsKey(name);
}

2

私にとってこれはうまくいきます:

if (IsProperty(() => DynamicObject.MyProperty))
  ; // do stuff



delegate string GetValueDelegate();

private bool IsProperty(GetValueDelegate getValueMethod)
{
    try
    {
        //we're not interesting in the return value.
        //What we need to know is whether an exception occurred or not

        var v = getValueMethod();
        return v != null;
    }
    catch (RuntimeBinderException)
    {
        return false;
    }
    catch
    {
        return true;
    }
}

nullプロパティが存在しないという意味ではありません
ケツァルコアトル2015

私は知っているけど、それがnullの場合、私はそれは大丈夫です、私のユースケースのために、したがって値で何もする必要はありません
ジェスター

0

動的として使用されているタイプを制御する場合、すべてのプロパティアクセスの値の代わりにタプルを返せませんか?何かのようなもの...

public class DynamicValue<T>
{
    internal DynamicValue(T value, bool exists)
    {
         Value = value;
         Exists = exists;
    }

    T Value { get; private set; }
    bool Exists { get; private set; }
}

おそらくナイーブな実装ですが、内部で毎回これらの1つを構築し、実際の値の代わりにそれを返す場合、Existsすべてのプロパティアクセスをチェックし、Valueそうでない場合は値があるdefault(T)(そして無関係である)場合にヒットできます。

とは言っても、動的がどのように機能するかについての知識が不足している可能性があり、これは実行可能な提案ではない可能性があります。


0

私の場合、特定の名前のメソッドの存在を確認する必要があったので、そのためのインターフェースを使用しました

var plugin = this.pluginFinder.GetPluginIfInstalled<IPlugin>(pluginName) as dynamic;
if (plugin != null && plugin is ICustomPluginAction)
{
    plugin.CustomPluginAction(action);
}

また、インターフェースにはメソッド以外のものも含めることができます。

インターフェイスには、メソッド、プロパティ、イベント、インデクサー、またはこれら4つのメンバータイプの任意の組み合わせを含めることができます。

From:インターフェイス(C#プログラミングガイド)

エレガントで、例外をトラップしたり、反射で遊んだりする必要はありません...


0

私はこれが本当に古い投稿であることを知っていますが、ここではdynamictype in を操作する簡単な解決策を示しc#ます。

  1. 単純なリフレクションを使用して直接プロパティを列挙できます
  2. またはobject拡張メソッド を使用できます
  3. または、GetAsOrDefault<int>メソッドを使用して、値が存在する場合は強く型付けされた新しいオブジェクトを取得し、存在しない場合はデフォルトを取得します。
public static class DynamicHelper
{
    private static void Test( )
    {
        dynamic myobj = new
                        {
                            myInt = 1,
                            myArray = new[ ]
                                      {
                                          1, 2.3
                                      },
                            myDict = new
                                     {
                                         myInt = 1
                                     }
                        };

        var myIntOrZero = myobj.GetAsOrDefault< int >( ( Func< int > )( ( ) => myobj.noExist ) );
        int? myNullableInt = GetAs< int >( myobj, ( Func< int > )( ( ) => myobj.myInt ) );

        if( default( int ) != myIntOrZero )
            Console.WriteLine( $"myInt: '{myIntOrZero}'" );

        if( default( int? ) != myNullableInt )
            Console.WriteLine( $"myInt: '{myNullableInt}'" );

        if( DoesPropertyExist( myobj, "myInt" ) )
            Console.WriteLine( $"myInt exists and it is: '{( int )myobj.myInt}'" );
    }

    public static bool DoesPropertyExist( dynamic dyn, string property )
    {
        var t = ( Type )dyn.GetType( );
        var props = t.GetProperties( );
        return props.Any( p => p.Name.Equals( property ) );
    }

    public static object GetAs< T >( dynamic obj, Func< T > lookup )
    {
        try
        {
            var val = lookup( );
            return ( T )val;
        }
        catch( RuntimeBinderException ) { }

        return null;
    }

    public static T GetAsOrDefault< T >( this object obj, Func< T > test )
    {
        try
        {
            var val = test( );
            return ( T )val;
        }
        catch( RuntimeBinderException ) { }

        return default( T );
    }
}

0

ExpandoObject継承するのでIDictionary<string, object>、次のチェックを使用できます

dynamic myVariable = GetDataThatLooksVerySimilarButNotTheSame();

if (((IDictionary<string, object>)myVariable).ContainsKey("MyProperty"))    
//Do stuff

このチェックを実行するユーティリティメソッドを作成できます。これにより、コードがよりクリーンで再利用可能になります。


-1

ここに他の方法があります:

using Newtonsoft.Json.Linq;

internal class DymanicTest
{
    public static string Json = @"{
            ""AED"": 3.672825,
            ""AFN"": 56.982875,
            ""ALL"": 110.252599,
            ""AMD"": 408.222002,
            ""ANG"": 1.78704,
            ""AOA"": 98.192249,
            ""ARS"": 8.44469
}";

    public static void Run()
    {
        dynamic dynamicObject = JObject.Parse(Json);

        foreach (JProperty variable in dynamicObject)
        {
            if (variable.Name == "AMD")
            {
                var value = variable.Value;
            }
        }
    }
}

2
問題はJObjectのプロパティのテストについての考えをどこで得ましたか?あなたの答えは、IEnumerableをプロパティで公開するオブジェクト/クラスに限定されます。によって保証されていませんdynamicdynamicキーワードはもっと広い主題です。あなたがテストできる場合ゴーチェックCountdynamic foo = new List<int>{ 1,2,3,4 }そのような
ケツァルコアトル
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.