Test if a property is available on a dynamic variable
Asked Answered
C

16

266

My situation is very simple. Somewhere in my code I have this:

dynamic myVariable = GetDataThatLooksVerySimilarButNotTheSame();

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

So, basically my question is how to check (without throwing an exception) that a certain property is available on my dynamic variable. I could do GetType() but I'd rather avoid that since I don't really need to know the type of the object. All that I really want to know is whether a property (or method, if that makes life easier) is available. Any pointers?

Colostomy answered 8/6, 2010 at 15:49 Comment(3)
There are a couple of suggestions here: #2985661 - but no accepted answer so far.Sixty
thanks, I can see how to make fir one of the solutions, tho I was wondering if there is anything I m missing outColostomy
Possible duplicate of How to detect if a property exists on an ExpandoObject?Latonia
S
180

I think there is no way to find out whether a dynamic variable has a certain member without trying to access it, unless you re-implemented the way dynamic binding is handled in the C# compiler. Which would probably include a lot of guessing, because it is implementation-defined, according to the C# specification.

So you should actually try to access the member and catch an exception, if it fails:

dynamic myVariable = GetDataThatLooksVerySimilarButNotTheSame();

try
{
    var x = myVariable.MyProperty;
    // do stuff with x
}
catch (RuntimeBinderException)
{
    //  MyProperty doesn't exist
} 
Snipes answered 24/4, 2011 at 3:23 Comment(17)
I ll mark this as the answer as its been so long, it does seem to be the best answerColostomy
Better solution - #2840098Biparous
@Biparous If you mean casting to IDictionary and work with that, that works only on ExpandoObject, it won't work on any other dynamic object.Snipes
Please see my answer for answer comparison.Leatherette
couldn't you also do if (dynVar.ToString(),Contains("PropName")) //do stuff ?Avowal
@Avowal Nope, most types don't return all their contents on ToString().Snipes
I actually just tried var t = dynVar.nonExistentProp; and t was null. No error was thrown.Avowal
@Avowal That means the type of dynVar considers all possible property names to be valid. Other types will act differently.Snipes
RuntimeBinderException is in the Microsoft.CSharp.RuntimeBinder namespace.Bistre
what if i have a logic depends on the existince of multiple properties in the dynamic object. I mean if(dynVar.Prop) { // do stuff } else if(dynVar.Prop2) { // do another } else{ // give alert }. Can i catch the which property absent in the catch block and give meaningful response?Sur
I still feel like using try/catch instead of if/else is bad practice in general regardless of this specifics of this scenario.Oceanid
So the accepted solution is to expect something exceptional to happen? Exceptions are costly, there must be a better way.Ifc
@DanielOrmeño There isn't, at least assuming you want to properly support everything that dynamic does (including e.g. ExpandoObject) and that you're unwilling to delve into the internals of the DLR.Snipes
In some cases I used NullReferenceExceptionAdult
Exceptions should be reserved for situations which shouldn't happen. Using exception in this way is a bad practiceChalcidice
@AlessioInnocenzi The runtime literally does not provide any other way to detect this.. If you have a problem with that, you need to be complaining to them, not in the comments of a Stack Overflow answer.Fuchsia
Generally speaking, exceptions should be for instances where the app CANNOT continue when an error happens (e.g. file not found, argument invalid). Exceptions are slow because it makes the CLR navigate the callstack -- I think casting your dynamic as IDictionary<string, object> and then doing a TryGetValue is going to be quicker. But by all means test it in a profiler -- maybe I'm wrong :-)Mcquade
L
86

I thought I'd do a comparison of Martijn's answer and svick's answer...

The following program returns the following results:

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"));
}

As a result I'd suggest using reflection. See below.


Responding to bland's comment:

Ratios are reflection:exception ticks for 100000 iterations:

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

...fair enough - if you expect it to fail with a probability with less than ~1/47, then go for exception.


The above assumes that you're running GetProperties() each time. You may be able to speed up the process by caching the result of GetProperties() for each type in a dictionary or similar. This may help if you're checking against the same set of types over and again.

Leatherette answered 15/11, 2013 at 12:43 Comment(9)
I agree and like reflection in my work where appropriate. The gains it has over Try/Catch is solely when the exception is thrown. So what someone should ask before using reflection here - is it likely to be a certain way? 90% or even 75% of the time, will your code pass? Then Try/Catch is still optimal. If its up in the air, or too many choices for one to be most likely, then your reflection is spot on.Mariannemariano
@Leatherette it's not fair to compare both since both behave differently. svick's answer is more complete.Margueritamarguerite
@Margueritamarguerite Of course it's fair to compare them - they behave differently but perform the same function! Mine isn't really an answer per se, as stayed it is a comparison to help people decide which of the methods presented to choose.Leatherette
@Leatherette No, they dont perform the same function. Martijn's answer checks if a property exist on a regular compile time type in C#, that is declared dynamic (meaning it ignores compile time safety checks). Whereas svick's answer checks if a property exists on a truly dynamic object, ie something that implements IIDynamicMetaObjectProvider. I do understand the motivation behind your answer, and appreciate it. It's fair to answer so.Margueritamarguerite
@Margueritamarguerite There's no requirement of that interface being implemented on either of the methods I compared.Leatherette
@Leatherette yes, but svick's answer covers that requirement too. Martijn's doesn't. So former had to go for try-catch, bound to be slower.Margueritamarguerite
@Leatherette hmm hard to assume given what answer OP accepted. Regardless, since they have different features it's not a fair comparison, I feel...Margueritamarguerite
@Leatherette - reflection checks may fail for some COM objects that behave like Expando (allow their property set to grow), while exception will do. Of course that's kind of niche, but still.Remaremain
How about Damian Powell answer? since he is not using exception nor refactoring ? Could you get the ticks for it ?Disaccredit
C
67

Maybe use reflection?

dynamic myVar = GetDataThatLooksVerySimilarButNotTheSame();
Type typeOfDynamic = myVar.GetType();
bool exist = typeOfDynamic.GetProperties().Where(p => p.Name.Equals("PropertyName")).Any(); 
Classics answered 3/5, 2011 at 12:30 Comment(6)
Quote from the question ". I could do GetType() but I'd rather avoid that"Colostomy
Doesn't this have the same drawbacks as my suggestion? RouteValueDictionary uses reflection to get properties.Radcliffe
You can just do without the Where: .Any(p => p.Name.Equals("PropertyName"))Leatherette
Please see my answer for answer comparison.Leatherette
As a one-liner: ((Type)myVar.GetType()).GetProperties().Any(x => x.Name.Equals("PropertyName")). The cast to type is required to make the compiler happy about the lambda.Shaw
+1 as you can make this a function and test it in one line like if (PropertyExists(myObject, "PropertyName")) { var x = myObject.PropertyName; }Tradelast
Y
53

Just in case it helps someone:

If the method GetDataThatLooksVerySimilarButNotTheSame() returns an ExpandoObject you can also cast to a IDictionary before checking.

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

if (((IDictionary<string, object>)test).ContainsKey("foo"))
{
    Console.WriteLine(test.foo);
}
Yungyunick answered 17/4, 2015 at 7:53 Comment(2)
Not sure why this answer doesn't have more votes, because it does exactly what was asked for (no exception throw or reflection).Arlinda
@Arlinda This answer is great if you know that your dynamic object is an ExpandoObject or something else which implements IDictionary<string,object> but if it happens to be something else, then this will fail.Hamlin
H
11

The two common solutions to this include making the call and catching the RuntimeBinderException, using reflection to check for the call, or serialising to a text format and parsing from there. The problem with exceptions is that they are very slow, because when one is constructed, the current call stack is serialised. Serialising to JSON or something analogous incurs a similar penalty. This leaves us with reflection but it only works if the underlying object is actually a POCO with real members on it. If it's a dynamic wrapper around a dictionary, a COM object, or an external web service, then reflection won't help.

Another solution is to use IDynamicMetaObjectProvider to get the member names as the DLR sees them. In the example below, I use a static class (Dynamic) to test for the Age field and display it.

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;
    }
}
Hamlin answered 28/7, 2016 at 11:37 Comment(2)
It turns out that the Dynamitey nuget package already does this. (nuget.org/packages/Dynamitey)Hamlin
Not working, tested on VS2022 and GetMembersName returns null parsing object with many propertiesGaberones
B
9

Denis's answer made me think to another solution using JsonObjects,

a header property checker:

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

or maybe better:

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

for example:

dynamic json = JsonConvert.DeserializeObject(data);
string header = hasHeader(json) ? json.header : null;
Baggs answered 12/6, 2015 at 10:54 Comment(4)
Is there a chance to know what is wrong with this answer please?Baggs
Don't know why this got voted down, worked great for me. I moved the Predicate for each property into a helper class and called the Invoke method to return a bool from each one.Bunn
@CharlesHETIER Because this question isn't about JSON.Fuchsia
@IanKemp I think you missed the purpose of the code here. This particular idea for solving the problem with Json API may not be the natural way, but it does offer a way of solving the question.Baggs
K
7

Well, I faced a similar problem but on unit tests.

Using SharpTestsEx you can check if a property existis. I use this testing my controllers, because since the JSON object is dynamic, someone can change the name and forget to change it in the javascript or something, so testing for all properties when writing the controller should increase my safety.

Example:

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

Now, using SharTestsEx:

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

Using this, i test all existing properties using "Should().NotThrow()".

It's probably out of topic, but can be usefull for someone.

Kayceekaye answered 13/1, 2012 at 18:53 Comment(1)
Thanks, very useful. Using SharpTestsEx I use the following line to also test the value of the dynamic property: ((string)(testedObject.MyName)).Should().Be("I am a testing object");Eve
G
1

For me this works:

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

    return true;
}
Greenness answered 6/12, 2014 at 20:8 Comment(3)
null does not mean the property does not existRemaremain
I know but if it is null I don't need to do anything with the value therefore for my usecase it is okGreenness
It looks like you're basically using a try-catch like an if-else conditionOocyte
A
1

Following on from the answer by @karask, you could wrap the function as a helper like so:

public static bool HasProperty(ExpandoObject expandoObj,
                               string name)
{
    return ((IDictionary<string, object>)expandoObj).ContainsKey(name);
}
Arlinda answered 21/6, 2016 at 8:14 Comment(0)
C
0

If you control the type being used as dynamic, couldn't you return a tuple instead of a value for every property access? Something like...

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

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

Possibly a naive implementation, but if you construct one of these internally each time and return that instead of the actual value, you can check Exists on every property access and then hit Value if it does with value being default(T) (and irrelevant) if it doesn't.

That said, I might be missing some knowledge on how dynamic works and this might not be a workable suggestion.

Cruel answered 3/5, 2011 at 15:4 Comment(0)
Q
0

If your use case is to convert an api response, carrying about only a few fields, you can use this:

var template = new { address = new { street = "" } };
var response = JsonConvert.DeserializeAnonymousType(await result.Content.ReadAsStringAsync(), template);

string street = response?.address?.street;
Quacksalver answered 1/9, 2022 at 20:22 Comment(0)
O
0

You could first covert it to a JObject and use the .ContainsKey() function to check if a property exists.

dynamic myVariable = GetDataThatLooksVerySimilarButNotTheSame();
var jObject = JObject.FromObject(myVariable);

if(jObject.ContainsKey("MyProperty")){
  // "MyProperty" exists
}

JObject is found in the Newtonsoft.Json.Linq namespace

Oocyte answered 28/3 at 23:21 Comment(0)
K
-1

Here is the other way:

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;
            }
        }
    }
}
Kelda answered 27/1, 2015 at 19:45 Comment(1)
where did you get the idea the question is about testing JObject's properties? Your answer is limited to objects/classes that expose IEnumerable over their properties. Not guaranteed by dynamic. dynamic keyword is much wider subject. Go check if you can test for Count in dynamic foo = new List<int>{ 1,2,3,4 } like thatRemaremain
G
-1

In my case, I needed to check for the existence of a method with a specific name, so I used an interface for that

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

Also, interfaces can contain more than just methods:

Interfaces can contain methods, properties, events, indexers, or any combination of those four member types.

From: Interfaces (C# Programming Guide)

Elegant and no need to trap exceptions or play with reflexion...

Geminate answered 11/7, 2018 at 10:50 Comment(0)
D
-1

I know this is really old post but here is a simple solution to work with dynamic type in c#.

  1. can use simple reflection to enumerate direct properties
  2. or can use the object extention method
  3. or use GetAsOrDefault<int> method to get a new strongly typed object with value if exists or default if not exists.
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 );
    }
}
Darvon answered 28/3, 2019 at 18:13 Comment(0)
E
-1

As ExpandoObject inherits the IDictionary<string, object> you can use the following check

dynamic myVariable = GetDataThatLooksVerySimilarButNotTheSame();

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

You can make a utility method to perform this check, that will make the code much cleaner and re-usable

Epicarp answered 27/12, 2019 at 8:53 Comment(1)
I think that's already been covered in other answers here stackoverflow.com/a/29693414 (along with potential issue in the comments) and here stackoverflow.com/a/37938989Opine

© 2022 - 2024 — McMap. All rights reserved.