Last active
October 7, 2016 10:26
-
-
Save breezhang/8954586 to your computer and use it in GitHub Desktop.
DynamicMetaObject
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//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