Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    text
    copied!<p>You can create a <code>DynamicObject</code> that forwards the calls it receives to a list of targets, in a chain of responsibility style (note that polymorphic dispatch also works like this - from the most derived class upwards):</p> <pre><code>public class Composition : DynamicObject { private List&lt;object&gt; targets = new List&lt;object&gt;(); public Composition(params object[] targets) { AddTargets(targets); } protected void AddTargets(IEnumerable&lt;object&gt; targets) { this.targets.AddRange(targets); } public override bool TryInvokeMember( InvokeMemberBinder binder, object[] args, out object result) { foreach (var target in targets) { var methods = target.GetType().GetMethods(); var targetMethod = methods.FirstOrDefault(m =&gt; m.Name == binder.Name &amp;&amp; ParametersMatch(m, args)); if (targetMethod != null) { result = targetMethod.Invoke(target, args); return true; } } return base.TryInvokeMember(binder, args, out result); } private bool ParametersMatch(MethodInfo method, object[] args) { var typesAreTheSame = method.GetParameters().Zip( args, (param, arg) =&gt; param.GetType() == arg.GetType()); return typesAreTheSame.Count() == args.Length &amp;&amp; typesAreTheSame.All(_=&gt;_); } } </code></pre> <p>Note that you'd also want to implement delegation for properties (<code>TryGetMember</code> and <code>TrySetMember</code>), indexers (<code>TryGetIndex</code> and <code>TrySetIndex</code>) and operators (<code>TryBinaryOperation</code> and <code>TryUnaryOperation</code>).</p> <p>Then, given a set of classes:</p> <pre><code>class MyClass { public void MyClassMethod() { Console.WriteLine("MyClass::Method"); } } class MyOtherClass { public void MyOtherClassMethod() { Console.WriteLine("MyOtherClass::Method"); } } </code></pre> <p>You can "blend" them all together:</p> <pre><code>dynamic blend = new Composition(new MyClass(), new MyOtherClass()); blend.MyClassMethod(); blend.MyOtherClassMethod(); </code></pre> <p>You can also extend the dynamic object to use classes' attributes or other kinds of annotations to look for mixins. For example, given this annotation interface:</p> <pre><code>public interface Uses&lt;M&gt; where M : new() { } </code></pre> <p>You can have this <code>DynamicObject</code>:</p> <pre><code>public class MixinComposition : Composition { public MixinComposition(object target) : base(target) { AddTargets(ResolveMixins(target.GetType())); } private IEnumerable&lt;object&gt; ResolveMixins(Type mainType) { return ResolveMixinTypes(mainType). Select(m =&gt; InstantiateMixin(m)); } private IEnumerable&lt;Type&gt; ResolveMixinTypes(Type mainType) { return mainType.GetInterfaces(). Where(i =&gt; i.IsGenericType &amp;&amp; i.GetGenericTypeDefinition() == typeof(Uses&lt;&gt;)). Select(u =&gt; u.GetGenericArguments()[0]); } private object InstantiateMixin(Type type) { return Activator.CreateInstance(type); } } </code></pre> <p>And create your "blends" like this:</p> <pre><code>class MyMixin { public void MyMixinMethod() { Console.WriteLine("MyMixin::Method"); } } class MyClass : Uses&lt;MyMixin&gt; { public void MyClassMethod() { Console.WriteLine("MyClass::Method"); } } ... dynamic blend = new MixinComposition(new MyClass()); blend.MyClassMethod(); blend.MyMixinMethod(); </code></pre>
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload