Monday, March 27, 2006

Illustration of the Linq "Expressions Tree" and of the former classes

As an illustration of what is an Linq "Expression Tree", I will demonstrate here how to go from a simple one line declaration in a source file : Expression < Func < BusinessDAO , IEnumerable >> FunctionExpression = mydata => mydata.Products.Where(p => p.UnitPrice.Length == 4); to a visual representation of that code : As an illustration of the previous post, I managed to go from here to there in two different ways.
  • Once using the "Visitor pattern" to the Expression :

In that case, the tree structure is "implicit" and is handled internally by the visitor logic

  • Once with a classical Tree obtained using the wrapper exposed on the previous post :

Here the tree is accessed direcly on the ExpressionTree object itself, as the role of the wrapper is to explicit the implicit tree structure, and expose it trhough a "children" property. I hope this make it clearer what expressions are.

So now, the interesting question : which usefulness do you envision for them in your projects .....? ;)

[+/-] read/hide the code

Form1.cs

using System;
using System.Collections;
using System.Collections.Generic;
using System.Expressions;
using System.Query;
using System.Windows.Forms;
using DataStructure;

namespace ExpressionIllustration
{



    public partial class Form1 : Form
    {
        BusinessDAO data;
        public Form1()
        {
            InitializeComponent();
            data = new BusinessDAO();
        }


        private void button1_Click(object sender, EventArgs e)
        {
            Expression<Func<BusinessDAO, IEnumerable>> FunctionExpression =mydata => mydata.Products.Where(p => p.ProductName.Length == 4);

            this.treeView1.Nodes.Clear();
            this.treeView1.Nodes.Add((TreeNode)FunctionExpression.Visit(new ExpressionTreeVisitor.VisitorDelegate(Utils.ToTreeNodeConverter), null));
        }

        private void button2_Click(object sender, EventArgs e)
        {
            Expression<Func<BusinessDAO, IEnumerable>> FunctionExpression =mydata => mydata.Products.Where(p => p.ProductName.Length == 4);
            ExpressionTree FunctionExpressionAsaTree = new ExpressionTree(FunctionExpression);

            this.treeView1.Nodes.Clear();
            this.treeView1.Nodes.Add(FunctionExpressionAsaTree.ToTreeNodes());
        }
    }
    public static class Utils
    {
        public static object ToTreeNodeConverter(Expression arg, object param, object[] sonreturn)
        {
            TreeNode ret = new TreeNode("Return Type : " +arg.Type.ToString());
            ret.Nodes.Add(new TreeNode("Expression : " + arg.ToString()));
            ret.Nodes.Add(new TreeNode("Expression Type: " + arg.NodeType.ToString()));
            TreeNode children = new TreeNode("Children");

            foreach (object son in sonreturn)
            {
                object[] group = son as object[];
                if (group != null)
                {
                    foreach (object obj in group)
                        children.Nodes.Add(obj as TreeNode);
                }
                else
                    children.Nodes.Add(son as TreeNode);
            }
         
            if(children.Nodes.Count >0)
                ret.Nodes.Add(children);
            return ret;
        }

        public static TreeNode ToTreeNodes(this ExpressionTree arg)
        {
            TreeNode ret = new TreeNode(arg.Type.ToString());
            ret.Nodes.Add(new TreeNode("Expression : " + arg.Value.ToString()));
            TreeNode children = new TreeNode("Children");

            foreach (ExpressionTree son in arg.Children)
                children.Nodes.Add(son.ToTreeNodes());

            ret.Nodes.Add(children);
            return ret;
        }
    }

    public partial class Product
    {
        private string _ProductName;
        private double _UnitPrice;
        public string ProductName
        {
            get { return _ProductName; }
            set { _ProductName = value; }
        }
        public double UnitPrice
        {
            get { return _UnitPrice; }
            set { _UnitPrice = value; }
        }
    }

    public class BusinessDAO
    {
        Product[] aproducts = new Product[2];

        public BusinessDAO()
        {
            aproducts[0] = new Product { ProductName = "1", UnitPrice = 1 };
            aproducts[1] = new Product { ProductName = "12", UnitPrice = 2 };
        }

        public IEnumerable<Product> Products
        {
            get { return aproducts; }
        }
    }
}
 

Saturday, March 25, 2006

Expression Trees and Linq

I love this new technology called "Linq", not only does it make some things much simpler, it also allows new possibilities. While upon writing traditionnal code you get a hold on the end result function, with Expression Tree you can get a hold on the abstract composition which your function is made of. The actual operation performed by your instruction, or sequence of, is just an aspect of it. One can in principle, manipulate, enrich the sequence before transforming it to an actual function... You can think of it as an extension of delegates. While delegates enables you to defer the actual execution of a command, Expression Trees enables you to also defer the actual body content. Practically, those operations are not natively possible with Linq alone. You need :
  • A way to travel in the tree structure of the Expression tree : you can start with the code below. feel free to post back some if you improve it.
  • A way to convert from the Expression tree to actual code : the espresso sample provides a first start too. May be I'll post some more complete code here.

So here is a wrapper to the Linq Expression type I wrote to explicit the tree structure.I allows you to traverse the tree for transformation.

Possible improvements would be to

  • support grouping of children node
  • Adding event support (like adding, removing element..) to make it bindable

[+/-] read/hide the code

ExpressionTree.cs

using System;
using System.Collections.Generic;
using System.Expressions;

namespace DataStructure
{

   [Flags]
   public enum ExpressionTypeTree : long
   {
       Add = 0,
       And = 2,
       As = 4,
       BitwiseAnd = 8,
       BitwiseNot = 16,
       BitwiseOr = 32,
       BitwiseXor = 64,
       Cast = 128,
       Coalesce = 256,
       Concat = 512,
       Constant = 1024,
       Convert = 2048,
       Divide = 4096,
       EQ = 8192,
       Funclet = 16384,
       GT = 32768,
       GE = 65536,
       Index = 131072,
       Invoke = 262144,
       Is = 524488,
       Lambda = 1048576,
       LE = 2097152,
       Len = 4194304,
       LShift = 8388608,
       LT = 16777216,
       MemberAccess = 33554432,
       MethodCall = 67108864,
       Modulo = 134217728,
       Multiply = 268435456,
       Negate = 536870912,
       NE = 1073741824,
       New = 2147483648,
       NewArrayInit = 4294967296,
       NewArrayBounds = 8589934592,
       Not = 17179869184,
       Or = 34359738368,
       Parameter = 68719476736,
       RShift = 137438953472,
       Source = 274877906944,
       Subtract = 549755813888,
       All = 1099511627775,
   }

   public class ExpressionTreeVisitor
   {
       public delegate object VisitorDelegate(Expression e, object param, object[] sonreturn);

       public VisitorDelegate VisitorNew;
       public VisitorDelegate VisitorBitwiseNot;
       public VisitorDelegate VisitorNegate;
       public VisitorDelegate VisitorNot;
       public VisitorDelegate VisitorAdd;
       public VisitorDelegate VisitorSubtract;
       public VisitorDelegate VisitorMultiply;
       public VisitorDelegate VisitorDivide;
       public VisitorDelegate VisitorModulo;
       public VisitorDelegate VisitorAnd;
       public VisitorDelegate VisitorOr;
       public VisitorDelegate VisitorLT;
       public VisitorDelegate VisitorLE;
       public VisitorDelegate VisitorGT;
       public VisitorDelegate VisitorGE;
       public VisitorDelegate VisitorEQ;
       public VisitorDelegate VisitorNE;
       public VisitorDelegate VisitorBitwiseAnd;
       public VisitorDelegate VisitorBitwiseOr;
       public VisitorDelegate VisitorBitwiseXor;
       public VisitorDelegate VisitorCoalesce;
       public VisitorDelegate VisitorConstant;
       public VisitorDelegate VisitorMemberAccess;
       public VisitorDelegate VisitorLambda;
       public VisitorDelegate VisitorParameter;
       public VisitorDelegate VisitorMethodCall;
       public VisitorDelegate VisitorCast;
       public VisitorDelegate VisitorConcat;
       public VisitorDelegate VisitorFunclet;
       public VisitorDelegate VisitorNewArrayInit;
       public VisitorDelegate VisitorDefault;


       public virtual void AssignVisitorTo(VisitorDelegate v, ExpressionTypeTree e)
       {
           if ((e & ExpressionTypeTree.New) != 0)
               VisitorNew = v;
           if ((e & ExpressionTypeTree.BitwiseNot) != 0)
               VisitorBitwiseNot = v;
           if ((e & ExpressionTypeTree.Negate) != 0)
               VisitorNegate = v;
           if ((e & ExpressionTypeTree.Not) != 0)
               VisitorNot = v;
           if ((e & ExpressionTypeTree.Add) != 0)
               VisitorAdd = v;
           if ((e & ExpressionTypeTree.Subtract) != 0)
               VisitorSubtract = v;
           if ((e & ExpressionTypeTree.Multiply) != 0)
               VisitorMultiply = v;
           if ((e & ExpressionTypeTree.Divide) != 0)
               VisitorDivide = v;
           if ((e & ExpressionTypeTree.Modulo) != 0)
               VisitorModulo = v;
           if ((e & ExpressionTypeTree.And) != 0)
               VisitorAnd = v;
           if ((e & ExpressionTypeTree.Or) != 0)
               VisitorOr = v;
           if ((e & ExpressionTypeTree.LT) != 0)
               VisitorLT = v;
           if ((e & ExpressionTypeTree.LE) != 0)
               VisitorLE = v;
           if ((e & ExpressionTypeTree.GT) != 0)
               VisitorGT = v;
           if ((e & ExpressionTypeTree.GE) != 0)
               VisitorGE = v;
           if ((e & ExpressionTypeTree.EQ) != 0)
               VisitorEQ = v;
           if ((e & ExpressionTypeTree.NE) != 0)
               VisitorNE = v;
           if ((e & ExpressionTypeTree.BitwiseAnd) != 0)
               VisitorBitwiseAnd = v;
           if ((e & ExpressionTypeTree.BitwiseOr) != 0)
               VisitorBitwiseOr = v;
           if ((e & ExpressionTypeTree.BitwiseXor) != 0)
               VisitorBitwiseXor = v;
           if ((e & ExpressionTypeTree.Coalesce) != 0)
               VisitorCoalesce = v;
           if ((e & ExpressionTypeTree.Constant) != 0)
               VisitorConstant = v;
           if ((e & ExpressionTypeTree.MemberAccess) != 0)
               VisitorMemberAccess = v;
           if ((e & ExpressionTypeTree.Lambda) != 0)
               VisitorLambda = v;
           if ((e & ExpressionTypeTree.Parameter) != 0)
               VisitorParameter = v;
           if ((e & ExpressionTypeTree.MethodCall) != 0)
               VisitorMethodCall = v;
           if ((e & ExpressionTypeTree.Cast) != 0)
               VisitorCast = v;
           if ((e & ExpressionTypeTree.Concat) != 0)
               VisitorConcat = v;
           if ((e & ExpressionTypeTree.Funclet) != 0)
               VisitorFunclet = v;
           if ((e & ExpressionTypeTree.NewArrayInit) != 0)
               VisitorNewArrayInit = v;
       }

       public object Visit(Expression e, object param)
       {
           switch (e.NodeType)
           {
               case ExpressionType.New:
               {
                   NewExpression ne = e as NewExpression;
                   object[] res1 = ne.Args.VisitList(this, param);
                   object[] res2 = ne.Bindings.VisitList(this, param);
                   return this.VisitorNew(e, param, new object[][] { res1, res2 });
               }
               case ExpressionType.BitwiseNot:
                   UnaryExpression ne1 = e as UnaryExpression;
                   return this.VisitorBitwiseNot(e, param, new object[] { ne1.Operand.Visit(this, param) });
               case ExpressionType.Negate:
                   UnaryExpression ne2 = e as UnaryExpression;
                   return this.VisitorNegate(e, param, new object[] { ne2.Operand.Visit(this, param) });
               case ExpressionType.Not:
                   UnaryExpression ne3 = e as UnaryExpression;
                   return this.VisitorNot(e, param, new object[] { ne3.Operand.Visit(this, param) });
               case ExpressionType.Add:
               case ExpressionType.Subtract:
               case ExpressionType.Multiply:
               case ExpressionType.Divide:
               case ExpressionType.Modulo:
               case ExpressionType.And:
               case ExpressionType.Or:
               case ExpressionType.LT:
               case ExpressionType.LE:
               case ExpressionType.GT:
               case ExpressionType.GE:
               case ExpressionType.EQ:
               case ExpressionType.NE:
               case ExpressionType.BitwiseAnd:
               case ExpressionType.BitwiseOr:
               case ExpressionType.BitwiseXor:
               case ExpressionType.Coalesce:
                   return this.VisitBinary((e as BinaryExpression), param);
               case ExpressionType.Constant:
                   return this.VisitorConstant(e, param, new object[] { });
               case ExpressionType.MemberAccess:
                   return this.VisitorMemberAccess(e, param, new object[] { (e as MemberExpression).Expression.Visit(this, param) });
               case ExpressionType.Lambda:
               {
                   LambdaExpression le = e as LambdaExpression;
                   object[] res1 = le.Parameters.VisitList(this, param);
                   object res2 = le.Body.Visit(this, param);
                   return this.VisitorLambda(e, param, new object[] { res1, res2 });
               }
               case ExpressionType.Parameter:
                   return this.VisitorParameter(e, param, new object[] { });
               case ExpressionType.MethodCall:
               {
                   MethodCallExpression me = e as MethodCallExpression;
                   object[] res1 = me.Parameters.VisitList(this, param);

                   return this.VisitorMethodCall(e, param, res1);
               }
               case ExpressionType.Cast:
                   return this.VisitorCast(e, param, new object[] { (e as UnaryExpression).Operand.Visit(this, param) });
               case ExpressionType.Concat:
               {
                   NAryExpression ne = e as NAryExpression;
                   object[] res1 = ne.Expressions.VisitList(this, param);
                   return this.VisitorConcat(e, param, res1);
               }
               case ExpressionType.Funclet:
                   return this.VisitorFunclet(e, param, null);
               case ExpressionType.NewArrayInit :
               {
                   NewArrayExpression ne = e as NewArrayExpression;
                   object[] res1 = ne.Expressions.VisitList(this, param);
                   return this.VisitorNewArrayInit(e, param, res1);
               }
               default:
                   return this.VisitorDefault(e, param, new object[] { });
           }
       }
       private object VisitBinary(BinaryExpression e, object param)
       {
           object[] ret = new object[2];
           ret[0] = e.Left.Visit(this, param);
           ret[1] = e.Right.Visit(this, param);
           switch (e.NodeType)
           {
               case ExpressionType.Add:
                   return this.VisitorAdd(e, param, ret);
               case ExpressionType.Subtract:
                   return this.VisitorSubtract(e, param, ret);
               case ExpressionType.Multiply:
                   return this.VisitorMultiply(e, param, ret);
               case ExpressionType.Divide:
                   return this.VisitorDivide(e, param, ret);
               case ExpressionType.Modulo:
                   return this.VisitorModulo(e, param, ret);
               case ExpressionType.And:
                   return this.VisitorAnd(e, param, ret);
               case ExpressionType.Or:
                   return this.VisitorOr(e, param, ret);
               case ExpressionType.LT:
                   return this.VisitorLT(e, param, ret);
               case ExpressionType.LE:
                   return this.VisitorLE(e, param, ret);
               case ExpressionType.GT:
                   return this.VisitorGT(e, param, ret);
               case ExpressionType.GE:
                   return this.VisitorGE(e, param, ret);
               case ExpressionType.EQ:
                   return this.VisitorEQ(e, param, ret);
               case ExpressionType.NE:
                   return this.VisitorNE(e, param, ret);
               case ExpressionType.BitwiseAnd:
                   return this.VisitorBitwiseAnd(e, param, ret);
               case ExpressionType.BitwiseOr:
                   return this.VisitorBitwiseOr(e, param, ret);
               case ExpressionType.BitwiseXor:
                   return this.VisitorBitwiseXor(e, param, ret);
               case ExpressionType.Coalesce:
                   return this.VisitorCoalesce(e, param, ret);
           }
           return null;
       }
   }


   public static partial class Utils
   {
       public static object[] VisitList(this StaticList<Expression> el, ExpressionTreeVisitor etvisitor, object param)
       {
           object[] ret = new object[el.Count];
           for (int i = 0, n = el.Count; i < n; i++)
               ret[i] = el[i].Visit(etvisitor, param);

           return ret;
       }
       public static object[] VisitList(this StaticList<Binding> el, ExpressionTreeVisitor etvisitor, object param)
       {
           object[] ret = new object[el.Count];
           for (int i = 0, n = el.Count; i < n; i++)
           {
               ret[i] = el[i].Visit(etvisitor, param);
           }
           return ret;
       }
       public static object[] VisitList(this StaticList<ParameterExpression> el, ExpressionTreeVisitor etvisitor, object param)
       {
           object[] ret = new object[el.Count];
           for (int i = 0, n = el.Count; i < n; i++)
           {
               ret[i] = (el[i] as Expression).Visit(etvisitor, param);
           }
           return ret;
       }
       public static object Visit(this Binding e, ExpressionTreeVisitor etvisitor, object param)
       {
           return null;
       }
       public static object Visit(this Expression e, ExpressionTreeVisitor etvisitor, object param)
       {
           return etvisitor.Visit(e, param);
       }
       public static object Visit(this Expression e, ExpressionTreeVisitor.VisitorDelegate etvisitordelegate, object param)
       {
           ExpressionTreeVisitor visitor = new ExpressionTreeVisitor();
           visitor.AssignVisitorTo(etvisitordelegate, ExpressionTypeTree.All);
           return e.Visit(visitor, param);
       }
   }


   public class ExpressionTree : Expression, INode<Expression>
   {
       ExpressionTreeList _Children = new ExpressionTreeList();
       Expression _expression;
       bool _isdirty = false;
       bool _isbuilding = false;

       public static object TreeBuilder(Expression expr, object param, object[] sons)
       {
           ExpressionTree exprtree = param as ExpressionTree;
           if (exprtree.Value == expr)
           {
               for (int i = 0, n = sons.Length; i < n; i++)
               {
                   object[] group = sons[i] as object[];
                   if (group != null)
                   {
                       foreach (object obj in group)
                           exprtree.Children.Add(new ExpressionTree(obj as Expression));
                   }
                   else
                       exprtree.Children.Add(new ExpressionTree(sons[i] as Expression));
               }
           }
           return expr;
       }


       public ExpressionTree(Expression expr)
           : base(expr.NodeType, expr.Type)
       {
           Value = expr;
       }

       private void BuildTree()
       {
           _isbuilding = true;
           ExpressionTreeVisitor visitor = new ExpressionTreeVisitor();
           visitor.AssignVisitorTo(new ExpressionTreeVisitor.VisitorDelegate(TreeBuilder), ExpressionTypeTree.All);
           _expression.Visit(visitor, this);
           _isbuilding = false;
           _isdirty = false;
       }


       public Expression Value
       {
           get
           {
               return _expression;
           }
           set
           {
               _expression = value;
               _isdirty = true;
           }
       }
       public INodeList<Expression> Children
       {
           get
           {
               if (_isdirty && !_isbuilding) BuildTree();
               return _Children as INodeList<Expression>;
           }
           set
           {
               _Children = value as ExpressionTreeList;
           }
       }
   }


   public class ExpressionTreeList : List<INode<Expression>>, INodeList<Expression>
   {

   }

}

and the tree structure:

Tree.cs

using System;
using System.Collections.Generic;
using System.Collections;
using System.Windows.Forms;

namespace DataStructure
{
   public interface INode
   {
       object Value { get; set;}
       ICollection Children { get;}
   }
   public interface INodeList : IList<INode> { }
   public interface INode<T>
   {
       T Value { get;            set;        }
       INodeList<T> Children
       {
           get;
           set;
       }
   }
   public interface INodeList<T> : IList<INode<T>>
   {
   }

   public class Node<T> : INode
   {
       private T data;
       private NodeList<T> _children = null;

       public Node() { }
       public Node(T data) : this(data, null) { }
       public Node(T data, NodeList<T> children)
       {
           this.data = data;
           this._children = children;
       }

       public T Value
       {
           get
           {
               return data;
           }
           set
           {
               data = value;
           }
       }

       public NodeList<T> Children
       {
           get
           {
               return _children;
           }
           set
           {
               _children = value;
           }
       }

       #region INode Members

       object INode.Value
       {
           get
           {
               return this.Value;
           }
           set
           {
               this.Value = (T)value;
           }
       }

       ICollection INode.Children
       {
           get { return this.Children; }
       }

       #endregion
   }
   public class NodeList<T> : List<Node<T>>
   {
       public NodeList() : base() { }

       public NodeList(int initialSize)
       {
           for (int i = 0; i < initialSize; i++)
               base.Add(default(Node<T>));
       }

       public Node<T> FindByValue(T value)
       {
           foreach (Node<T> node in this)
               if (node.Value.Equals(value))
                   return node;
           return null;
       }
   }

   public delegate object TreeVisitor<T>(INode<T> arg, object param, object[] sonreturn);
   public delegate INode<T> TreeTransformer<T>(INode<T> arg, object param, INode<T>[] sonreturn);



   public static partial class Utils
   {
       public static object Visit<T>(this INode<T> arg, TreeVisitor<T> treevisitor, object param)
       {
           return treevisitor(arg, param, arg.Children.VisitList<T>(treevisitor, param));
       }
       public static object[] VisitList<T>(this INodeList<T> arg, TreeVisitor<T> treevisitor, object param)
       {
           object[] ret = new object[arg.Count];
           for (int i = 0, n = arg.Count; i < n; i++)
           {
               ret[i] = arg[i].Visit(treevisitor, param);
           }
           return ret;
       }
       public static INode<T> Transform<T>(this INode<T> arg, TreeTransformer<T> treetransformer, object param)
       {
           return treetransformer(arg, param, arg.Children.TransformList<T>(treetransformer, param));
       }
       public static INode<T>[] TransformList<T>(this INodeList<T> arg, TreeTransformer<T> treetransformer, object param)
       {
           INode<T>[] ret = new INode<T>[arg.Count];
           for (int i = 0, n = arg.Count; i < n; i++)
           {
               ret[i] = arg[i].Transform(treetransformer, param);
           }
           return ret;
       }

       public static INode<T> Copy<T>(INode<T> arg, object param, INode<T>[] sonreturn)
       {
           Node<T> ret = new Node<T>();
           foreach (Node<T> son in sonreturn)
               ret.Children.Add(son);
           return ret as INode<T>;
       }
       public static object ToTreeNodes<T>(INode<T> arg, object param, object[] sonreturn)
       {
           TreeNode ret = new TreeNode(arg.ToString() + ":" + arg.Value.ToString());
           foreach (object son in sonreturn)
               ret.Nodes.Add(son as TreeNode);
           return ret;
       }
   }
}

Thursday, March 09, 2006

About schyzophreny, C#3 intelligence and runtime stupidity

On my way toward a flexible application to perform queries on the vast amount of information my trading desk produces everyday, I was delighted to meet with C#3 and Linq functionnalities. Design time I always thought such a transversal approach was needed and I was surprised to remember that I developped a similar API for VB back in the old days :) Now I could have cutting edge Microsoft functionnalities and exceptionnal compiler intelligence working for me. Just think about this :
var queries = new List<IEnumerable>(); var query1 = this.GetCustomerList().Where( p => p.CompanyName.Length == 4); queries.Add(query1);

This simple line declare function, builds a query, infers return type... Also while I was typing it, I benefited from code completion, and if I add tried to assign it to a wrongly typed variable, I would have been warned of such probable mistake.What a great endeavour from a smal code line ! Yeah but as you may see from my code, I am not a business genius. My query does not add a lot of value for the Market Intelligence people in search of which customer is important. They'd probably So I'd like my users to type in their own queries. That way, market intelligence people will build their own queries, managers their have own other set too, and I wont coerce them into my limited imagination. Runtime definition of queries If I want to do this, here is how I could do to make it work :
  • define a langage
  • build a parser to go from string to a tree of command (an expression tree in Linq)
  • build a visitor for this tree to build the code to run using reflection
  • evaluate

Compare this with the ease I add with the previous query building. And with this solution I have lost all the intellisense, and many other prerun features....

Now what has changed from a functionnal point of view? Nothing, I just want to add some queries to my list...

Beside, all the logic I want my to present to my users already exists in the C# compiler.. Although it is very interesting, why would I need to reinvent the wheel? Partial runtime and dynamic compilation The idea is that I want to tag part of my code, the one which define the queries, as being modifiable, until my program really needs it.
var queries = new List<IEnumerable>(); var query1 = this.GetCustomerList().Where( p => p.CompanyName.Length == 4); queries.Add(query1); [userpoint("DefiningQueries")] foreach (var query1 in queries) { ..
If this tag is seen, I want the compiler to validate my code, and to store it as an expression, not compile it. Then, at runtime, I want to be able to make all the variables available at this place in my code known in a reflection-like manner, only this time reflection is not linked to an assembly or a class, but to a location in code. This helps me pre-validate the user input. And I also want to inject code, and compile it in the specific context of that [Userpoint] !
public void AddExpression(string expr) { CompilerError[] ces = Microsoft.CSharp.Compile(expr, this, "DefiningQueries"); var queries = Code.Userpoint("DefiningQueries").getVariable("queries"); queries.MethodCall("Add", new objet[] { ces } ); }
That way, in a first part of the program, I load my general form, and the environment which enables my users to edit their queries. When a query is added, it is actually added as parsed some code to my section. I make full reuse of everything that already exists at the csharp compiler level. So do you think this concept of partial runtime can be useful in other cases?