Problem
My problem is straightforward. This is somewhere in my code:
dynamic myVariable = GetDataThatLooksVerySimilarButNotTheSame();
//How to do this?
if (myVariable.MyProperty.Exists)
//Do stuff
So, basically, I’m trying to figure out how to verify (without throwing an exception) whether a specific property on my dynamic variable is available. I could use GetType(), but I’d like not because I don’t need to know the object’s type. All I actually want to know is whether a property (or, if that’s easier, a method) is available. Do you have any suggestions?
Asked by roundcrisis
Solution #1
Unless you re-implemented the way dynamic binding is handled in the C# compiler, I believe there is no way to figure out whether a dynamic variable contains a specific member without trying to access it. Because it is implementation-defined, according to the C# specification, there would almost certainly be a lot of guessing involved.
So, if you try to access the member and it fails, you should catch an exception:
dynamic myVariable = GetDataThatLooksVerySimilarButNotTheSame();
try
{
var x = myVariable.MyProperty;
// do stuff with x
}
catch (RuntimeBinderException)
{
// MyProperty doesn't exist
}
Answered by svick
Solution #2
I decided to compare Martijn’s response to svick’s response…
The results of the following program are as follows:
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 recommend that you reflect. Take a look at what follows.
In response to bland’s remark, I’d like to say the following:
For 100000 iterations, ratios are reflection:exception ticks:
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
…good enough – go for exception if you expect it to fail with a chance of less than 1/47.
The preceding assumes that GetProperties() is called every time. You might be able to speed things up by saving the GetProperties() results for each type in a dictionary or something similar. This may be useful if you’re constantly verifying against the same collection of types.
Answered by dav_i
Solution #3
Maybe use reflection?
dynamic myVar = GetDataThatLooksVerySimilarButNotTheSame();
Type typeOfDynamic = myVar.GetType();
bool exist = typeOfDynamic.GetProperties().Where(p => p.Name.Equals("PropertyName")).Any();
Answered by Martijn
Solution #4
In case it’s of any use to anyone:
You can also cast to an IDictionary before testing if the method GetDataThatLooksVerySimilarButNotTheSame() returns an ExpandoObject.
dynamic test = new System.Dynamic.ExpandoObject();
test.foo = "bar";
if (((IDictionary<string, object>)test).ContainsKey("foo"))
{
Console.WriteLine(test.foo);
}
Answered by karask
Solution #5
Making the call and catching the RuntimeBinderException, utilizing reflection to check for the call, or serializing to a text format and parsing from there are the two most popular alternatives. The issue with exceptions is that they are slow to construct because the current call stack is serialized when one is created. A comparable penalty is incurred when serializing to JSON or something like. This leaves reflection, but it can only be used if the underlying object is a POCO with genuine members. Reflection won’t assist if it’s a dynamic wrapper around a dictionary, a COM object, or an external web service.
Another option is to use IDynamicMetaObjectProvider to acquire the names of the members as they appear in the DLR. I use a static class (Dynamic) to test for and display the Age property in the example below.
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;
}
}
Answered by Damian Powell
Post is based on https://stackoverflow.com/questions/2998954/test-if-a-property-is-available-on-a-dynamic-variable