Returning object for method call expression doesn't work

Aug 14, 2013 at 4:26 PM
Edited Aug 14, 2013 at 7:02 PM
I have the following return line in a "BindSetMember" method:
return new DynamicMetaObject(Expression.TypeAs(methodCall, binder.ReturnType), restrictions)
Which doesn't box the returned 'bool' value from 'methodCall' (gives a type conversion error). I expected that the return would be boxed into an object type (since the method returns 'bool', but the DLR callsite expects 'object'; the specification says boxing should occur). I've tried "Expression.Convert" also, but I keep getting a type conversion error. Why isn't this working? Am I being forced to create a lambda expression to forward the call!? :(

Thanks.

Update: I've tried this also, which doesn't work:
return new DynamicMetaObject(Expression.Convert(methodCall, typeof(object), new Func<bool, object>(b => (object)b).Method), restrictions);
and neither does this:
 return new DynamicMetaObject(Expression.Convert(methodCall, typeof(object), new Func<bool, object>(b => (bool?)b).Method), restrictions);
Aug 14, 2013 at 5:33 PM
I assume the error happens when the generated code is executed, not when it's generated?

I know that by convention, SetMemberBinder implementations should return an expression whose result is the value being assigned. This allows chaining of assignments in languages where that's supported: i.e. "a.b = a.c = 10". But I didn't think that was enforced. Either Expression.TypeAs or Expression.Convert should have resulted in boxing of the boolean value returned by the methodCall expression.
Aug 14, 2013 at 5:44 PM
Edited Aug 14, 2013 at 5:47 PM
Yes, correct.

I figured as well, but it doesn't. :) 'methodCall' calls a "bool SetProperty(...)" type method, but I found out that "object" is expected as a return type. I could wrap it in a lambda to forward the call and return object, but I thought converting the type this way SHOULD work, no? :/ I even tried a conversion method for Expression.Convert(), but same thing.
Aug 14, 2013 at 6:39 PM
Edited Aug 14, 2013 at 6:40 PM
CurtHagenlocher wrote:
I know that by convention, SetMemberBinder implementations should return an expression whose result is the value being assigned.
The result returned from an expression (in the DynamicMetaObject returned from SetMemberBinder) is always "object" - any other type gives an error, so I'm not sure what that means.
Aug 14, 2013 at 7:23 PM
Anything can be returned as "object" no matter what it's compile-time type is -- though of course, you'd need to box value types.

I've implemented a trivial SetMember binder to confirm that there's no special restriction on the object type being returned by the generated code. Here's the code I used:
    public class Handle : IDynamicMetaObjectProvider
    {
        public bool SetMember(string member, object value)
        {
            System.Console.WriteLine("Setting {0} to {1}", member, value);
            return false;
        }

        public DynamicMetaObject GetMetaObject(Expression parameter)
        {
            return new HandleMetaObject(parameter, this);
        }

        class HandleMetaObject : DynamicMetaObject
        {
            internal HandleMetaObject(Expression parameter, Handle value)
                : base(parameter, BindingRestrictions.GetTypeRestriction(parameter, typeof(Handle)), value)
            {
            }

            public override DynamicMetaObject BindSetMember(SetMemberBinder binder, DynamicMetaObject value)
            {
                return new DynamicMetaObject(
                    Expression.Convert(
                        Expression.Call(
                            Expression.Convert(Expression, typeof(Handle)),
                            typeof(Handle).GetMethod("SetMember"),
                            Expression.Constant(binder.Name),
                            Expression.Convert(value.Expression, typeof(object))),
                        typeof(object)),
                    Restrictions);
            }
        }
    }
Aug 14, 2013 at 7:49 PM
Edited Aug 14, 2013 at 7:50 PM
That is pretty much what I was trying to do ... however I found the issue. I had thought the conversion issue was due to the return type of the method expression, but I forgot I had a conversion expression for one of the method parameters as well - and it was that conversion that was in error. I have an explicit conversion to a value type from a given object, but upon further investigation, it turns out you have to convert "object" to the actual type before implicit conversions will work. ;)

Thanks for your time. ;)

(For the record, "TypeAs" does box, as does "Convert".)