Skip to content

Instantly share code, notes, and snippets.

@breezhang
Last active October 7, 2016 10:26
Show Gist options
  • Save breezhang/8954586 to your computer and use it in GitHub Desktop.
Save breezhang/8954586 to your computer and use it in GitHub Desktop.
DynamicMetaObject
//http://blog.tomasm.net/2009/11/07/forwarding-meta-object/
using System;
using System.Dynamic;
using System.Linq.Expressions;
using Xunit;
namespace ClassLibrary1
{
public sealed class ForwardingMetaObject : DynamicMetaObject {
private readonly DynamicMetaObject _metaForwardee;
public ForwardingMetaObject(
Expression expression,
BindingRestrictions restrictions,
object forwarder,
IDynamicMetaObjectProvider forwardee,
Func<Expression, Expression> forwardeeGetter
): base(expression, restrictions, forwarder) {
// We'll use forwardee's meta-object to bind dynamic operations.
_metaForwardee = forwardee.GetMetaObject(
forwardeeGetter(
Expression.Convert(expression, forwarder.GetType()) // [1]
)
);
}
// Restricts the target object's type to TForwarder.
// The meta-object we are forwarding to assumes that it gets an instance of TForwarder (see [1]).
// We need to ensure that the assumption holds.
private DynamicMetaObject AddRestrictions(DynamicMetaObject result) {
var restricted = new DynamicMetaObject(
result.Expression,
BindingRestrictions.GetTypeRestriction(Expression, Value.GetType()).Merge(result.Restrictions),
_metaForwardee.Value
);
return restricted;
}
// Forward all dynamic operations or some of them as needed //
public override DynamicMetaObject BindGetMember(GetMemberBinder binder) {
return AddRestrictions(_metaForwardee.BindGetMember(binder));
}
public override DynamicMetaObject BindSetMember(SetMemberBinder binder, DynamicMetaObject value) {
return AddRestrictions(_metaForwardee.BindSetMember(binder, value));
}
public override DynamicMetaObject BindDeleteMember(DeleteMemberBinder binder) {
return AddRestrictions(_metaForwardee.BindDeleteMember(binder));
}
public override DynamicMetaObject BindGetIndex(GetIndexBinder binder, DynamicMetaObject[] indexes) {
return AddRestrictions(_metaForwardee.BindGetIndex(binder, indexes));
}
public override DynamicMetaObject BindSetIndex(SetIndexBinder binder, DynamicMetaObject[] indexes, DynamicMetaObject value) {
return AddRestrictions(_metaForwardee.BindSetIndex(binder, indexes, value));
}
public override DynamicMetaObject BindDeleteIndex(DeleteIndexBinder binder, DynamicMetaObject[] indexes) {
return AddRestrictions(_metaForwardee.BindDeleteIndex(binder, indexes));
}
public override DynamicMetaObject BindInvokeMember(InvokeMemberBinder binder, DynamicMetaObject[] args) {
return AddRestrictions(_metaForwardee.BindInvokeMember(binder, args));
}
public override DynamicMetaObject BindInvoke(InvokeBinder binder, DynamicMetaObject[] args) {
return AddRestrictions(_metaForwardee.BindInvoke(binder, args));
}
public override DynamicMetaObject BindCreateInstance(CreateInstanceBinder binder, DynamicMetaObject[] args) {
return AddRestrictions(_metaForwardee.BindCreateInstance(binder, args));
}
public override DynamicMetaObject BindUnaryOperation(UnaryOperationBinder binder) {
return AddRestrictions(_metaForwardee.BindUnaryOperation(binder));
}
public override DynamicMetaObject BindBinaryOperation(BinaryOperationBinder binder, DynamicMetaObject arg) {
return AddRestrictions(_metaForwardee.BindBinaryOperation(binder, arg));
}
public override DynamicMetaObject BindConvert(ConvertBinder binder) {
return AddRestrictions(_metaForwardee.BindConvert(binder));
}
}
public class B : DynamicObject
{
private readonly string _name;
public B(string name)
{
_name = name;
}
public override bool TryGetMember(GetMemberBinder binder, out object result)
{
result = "got member " + _name + "." + binder.Name;
return true;
}
public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result)
{
result = "invoked member " + _name + "." + binder.Name;
return true;
}
public override bool TryConvert(ConvertBinder binder, out object result)
{
if (binder.Type == typeof(string))
{
result = _name;
return true;
}
else
{
result = null;
return false;
}
}
}
public class A : IDynamicMetaObjectProvider
{
private readonly B _b;
public B B { get { return _b; } }
public A(B b)
{
_b = b;
}
DynamicMetaObject IDynamicMetaObjectProvider.GetMetaObject(Expression parameter)
{
return new ForwardingMetaObject(parameter, BindingRestrictions.Empty, this, _b,
// B's meta-object needs to know where to find the instance of B it is operating on.
// Assuming that an instance of A is passed to the 'parameter' expression
// we get the corresponding instance of B by reading the "B" property.
exprA => Expression.Property(exprA, "B")
);
}
}
public class Class1 {
[Fact]
public void Test() {
B b1 = new B("b1");
B b2 = new B("b2");
dynamic a1 = new A(b1);
dynamic a2 = new A(b2);
Console.WriteLine(a1.SomeMember);
Console.WriteLine(a2.SomeMember);
Console.WriteLine(a1.SomeMethodCall());
Console.WriteLine(a1.OtherMethodCall());
Console.WriteLine((string)a1);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment