Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Define a calculated property on an expando object

I am working with expando object and I am trying to define a calculated property.

I know that I can define a simple property by doing something like the following:

dynamic myExpando = new ExpandoObject();
myExpando.TheAnswerToLifeTheUniverseAndEverything= 42;

Likewise, I can also define a method:

myExpando.GetTheQuestion = ((Func<string>)(() =>
        {
            return "How many road must a man walk down before we can call him a man?";
        }));  

When working with a standard object we can define a calculated property, ie define a property that will return the results of a custom method/calc. No need for an example.

I need to do something similar on my expando - having a property that actually calls a "Func" (or some other form of delegate, anything goes as soon as I can call a custom method and have a custom return type). So basically I need to invoke a method like in the second example BUT have it work like a property.

Basically I need to be able to call it with myExpando.GetTheQuestion instead of myExpando.GetTheQuestion(), while keeping the ability of defining a custom delegate as the property body.

Is there a way to do this? I belive that I could do this by using Expression trees, but I admit I am a little lost there. Can anyone provide some guidance on how to achieve this??


EDIT

Done some more research.. Unless there is some very specific class/interface/sintax that I don't know I am starting to think that the above is impossible. From what I get, the ExpandoObject class works by defining some methods that do the background plumbing - TryGetMember, TrySetMember and such. Now when "accessing a property" on the dynamic objetc, TryGetMember is the memeber that gets called. That member returns a value from a sort of inner dictionary (yes, I know... this is a little simplified but should give the idea)... no test on the type of value returned. This means that in my example myExpando.GetTheQuestion would return the original Func.

It would seem that since TryGetMember just returns a value, there is no way to make it "execute" the property code. To achieve that, you would need some sort of expression/lambda/func/action surrogate which value is actually the RESULT of a method. Which seems impossible (nor would make much sense unless I miss something - basically you would have a value that is set to a 'delegate' and then is get as the delegate return value???). Am I correct or this or I am missing something?

like image 529
SPArcheon Avatar asked Feb 11 '13 16:02

SPArcheon


1 Answers

You need to make your own ExpandoObject, by inheriting DynamicObject and overriding

public override bool TryGetMember(GetMemberBinder binder, out object result) and public override bool TrySetMember(SetMemberBinder binder, object value)

Implement TrySetMember to store the value in a private Dictionary<string,object> under binder.Name and use TryGetMember to retrieve it from that dictionary, that will give you a basic ExpandoObject. Then to give it the feature you need, add a check in TryGetMember, after you pull the object, to see if it is Delagate and then use reflection to see if it doesn't take any arguments. If both are true just cast to dynamic and add no arg invocation parenthesis and assign it to result.

public override bool TryGetMember(GetMemberBinder binder, out object result)
{
      if (_dictionary.TryGetValue(binder.Name, out result)){
           if(result is Delegate && /* some reflection check on args*/){
                result = ((dynamic)result)();
           }
      }
}

I have an open source framework ImpromptuInterface (in nuget) that has a non-sealed ImpromptuDictionary which you could start with as your ExpandoObject instead, especially if you need any of the more nuanced features of ExpandoObject such as gui binding support. It also has more dlr plumbing features you might find useful.

like image 141
jbtule Avatar answered Oct 16 '22 19:10

jbtule