-
-
Notifications
You must be signed in to change notification settings - Fork 382
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Support for dynamic object parameter/variable (ExpandoObject) #16
Comments
👍 Any news on this? |
No, sorry. For now I don't have any news. I have tried to post a question on stackoverflow but without success: I have found a way to solve this only using unsupported/private methods. And I really don't like it. |
Have you tried deriving from the public (abstract) class DynamicMetaObjectBinder in the Also, I have done some research. Consider the following test program: using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
namespace TestProject
{
public class Program
{
public static void Test()
{
dynamic test = "Hello";
object result = test.Length;
Console.WriteLine(result);
}
}
} When decompiled using ILSpy, what you get is this: using Microsoft.CSharp.RuntimeBinder;
using System;
using System.Linq.Expressions;
using System.Runtime.CompilerServices;
namespace TestProject
{
public class Program
{
[CompilerGenerated]
private static class <Test>o__SiteContainer0
{
public static CallSite<Func<CallSite, object, object>> <>p__Site1;
}
public static void Test()
{
object test = "Hello";
if (Program.<Test>o__SiteContainer0.<>p__Site1 == null)
{
Program.<Test>o__SiteContainer0.<>p__Site1 = CallSite<Func<CallSite, object, object>>.Create(Binder.GetMember(CSharpBinderFlags.None, "Length", typeof(Program), new CSharpArgumentInfo[]
{
CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null)
}));
}
object result = Program.<Test>o__SiteContainer0.<>p__Site1.Target(Program.<Test>o__SiteContainer0.<>p__Site1, test);
Console.WriteLine(result);
}
}
} Which appears to reveal the inner workings of the dynamic keyword. I've manually edited this to make it more readable and remove insignificant parts: public class Program
{
[CompilerGenerated]
private static class Container
{
public static CallSite<Func<CallSite, object, object>> Site;
}
public static void Test()
{
object test = "Hello";
if (Program.Container.Site == null)
{
Program.Container.Site = CallSite<Func<CallSite, object, object>>.Create(
Binder.GetMember(
CSharpBinderFlags.None,
"Length",
typeof(Program),
new CSharpArgumentInfo[]
{
CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null)
}));
}
object result = Program.Container.Site.Target(Program.Container.Site, test);
Console.WriteLine(result);
}
} Now I'm assuming that we're dealing with two things here:
I would think that the CallSite stuff is related to the caching mechanism, so let's strip that away: public static void Test()
{
object test = "Hello";
var callSite = CallSite<Func<CallSite, object, object>>.Create(
Binder.GetMember(
CSharpBinderFlags.None,
"Length",
typeof(Program),
new CSharpArgumentInfo[]
{
CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null)
}));
object result = callSite.Target(callSite, test);
Console.WriteLine(result);
} Digging into the CallSite code reveals that the Create method actually requires a CallSiteBinder, just like the one required by
However, what it does (thanks ILSpy again :D ) appears to be nothing more than creating a |
My similar project CSharpEval supports dynamics. I also used ILSpy to get a gist of what the compiler was doing. It probably isn't perfect, but it works. You just put the binder you created in an Expression.Dynamic. Property Getter: var binder = Binder.GetMember(
CSharpBinderFlags.None,
membername,
type,
new[] { CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null) }
);
Expression result = Expression.Dynamic(binder, typeof(object), instance); Property Setter (upon checking that the left hand expression is a dynamic): var dle = (DynamicExpression)le;
var membername = ((GetMemberBinder)dle.Binder).Name;
var instance = dle.Arguments[0];
var binder = Binder.SetMember(
CSharpBinderFlags.None,
membername,
type,
new[]
{
CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null),
CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null)
}
);
return Expression.Dynamic(binder, typeof(object), instance, re); Method Call: var binderM = Binder.InvokeMember(
CSharpBinderFlags.None,
membername,
null,
type,
expArgs.Select(x => CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null))
);
return Expression.Dynamic(binderM, typeof(object), expArgs); You also have to catch unary and binary expressions and handle dynamics accordingly: Binary: var expArgs = new List<Expression>() { le, re };
var binderM = Binder.BinaryOperation(CSharpBinderFlags.None, expressionType, le.Type,
new CSharpArgumentInfo[]
{
CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null),
CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null)
});
return Expression.Dynamic(binderM, typeof(object), expArgs); All this is in the AntlrParser branch, ExpressionEvaluator project, ExpressionHelper.cs. My code is terrible, btw, but hopefully you can get some information from it. |
Thank you Adam and Rupert for your comments! I really appreciate it. I usually don't like to use code marked as
So my idea is to support some kind of extension mechanism used to plugin special/custom implementations. I will try to work on this on the first occasion! P.S. Just don't understand why Microsoft doesn't write a better documentation and official support for dynamic 👎 ! |
Have you tried something lik this: http://stackoverflow.com/questions/3562088/c-sharp-4-dynamic-in-expression-trees ? Expression GenerateAdd(Expression left, Expression right)
{
if (left.Type == typeof(string) && right.Type == typeof(string))
{
return GenerateStaticMethodCall("Concat", left, right);
}
if (left.Type == typeof (object) || right.Type == typeof (object))
return GenerateDynamicBinary(ExpressionType.Add, left, right);
return Expression.Add(left, right);
}
Expression GenerateDynamicBinary(ExpressionType exprType, Expression left, Expression right)
{
var binder = Microsoft.CSharp.RuntimeBinder.Binder.BinaryOperation(
CSharpBinderFlags.None, exprType, GetType(),
new[]
{
CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null),
CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null)
});
return Expression.Dynamic(binder, typeof(object), left, right);
} I'm not sure if it is right solution, but this test code works now: dynamic a = 5;
dynamic b = 6;
dynamic obj = new { a, b };
var target = new Interpreter();
target.SetVariable("obj", obj);
Assert.AreEqual(a + b, target.Eval("obj.a + obj.b")); |
Thanks @pgolinski for your code.
https://msdn.microsoft.com/en-us/library/microsoft.csharp.runtimebinder.binder%28v=vs.110%29.aspx Anyway probably it is the only easy solution...the any other solution that I think of is to create a custom Binder class. Can I ask you to send a pull request, so I can easily merge it. thanks! |
The code I pasted is all I have changed, so a pull request made from it would be quite useless. |
👍 I don't like it either, but this might your best bet. That or risk venturing into the uncharted waters of Roslyn circa VS2015 CTP. |
Thanks to all! I'm just releasing version 1.3.3 with partial dynamic support (get properties and method invocation). |
See also:
http://stackoverflow.com/questions/21982040/using-dynamic-types-with-espresso
The text was updated successfully, but these errors were encountered: