Define a calculated property on an expando object
Asked Answered
C

1

7

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?

Crossbreed answered 11/2, 2013 at 16:28 Comment(0)
W
2

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.

Withers answered 11/2, 2013 at 17:5 Comment(2)
Yes, actually I though about a similar solution - but I was wondering if this should be possible without custom code (not that I couldn't add it - after all my expando object is already a custom inherited class) by using expression trees and such. Anyway, if no other way will be posted, I will accept this as the best fit ^_^Crossbreed
Ok, for now I will accept this. It is a workaround, but I suppose it is also the only way to have the "calculated property" behave like a normal property. Since I have already extended the base expando to support other behaviours (I use a safe expando that returns null for undefined properties), I guess that I could go with this (actually that was my first idea, but I posted the question anyway to see if I was missing something). Thank jbtule, I will also have a look at your lib - there is always something to learn.Crossbreed

© 2022 - 2024 — McMap. All rights reserved.