Linq: Possibilities
Jeff Brown commented on my Linq Options post:
Pity they didn't shoot for lexically-scoped blocks a la SmallTalk (or Ruby)... This approach with Expressions has the same control-flow limitations as anonymous delegates but you can omit the curly braces sometimes.
Linq is not just anonymous delegates. (I should be clear that I am mostly thinking about the abilities of Expression rather than the Langague Integrated Query here). It means that I can start doing some really stuff. For what it worth, there is such a thing as the ExecutionScope for linq, but I am not sure what it is supposed to do, as far as I can see, it is the entire lexical scope for the expression.
Here is a trivial example that shows what you can do with it. Assume that I have this work item (and saved action):
[Serializable]
public class WorkItem
{
string name;
string action;
string on;
public WorkItem(string name, string action, string on)
{
this.name = name;
this.action = action;
this.on = on;
}
public WorkItem() {}
public void DoAction()
{
Console.WriteLine(name +" "+action+" " +on);
}
}
[Serializable]
private class SavedAction
{
public object target;
public string method;
}
And I have this code:
public delegate void Act();
static void Main(string[] args)
{
WorkItem wi = new WorkItem("Ayende,", "write", "blog post");
Save("Temp.action",() => wi.DoAction());
Act act = Load("Temp.action");
act();
}
What is going on here? I am saving the labmda into a file in the Save(), then load and execute it in the next two statement. Sadly, Linq's Expression<T> are not serializable, which I consider a huge minus, but for this example, I worked around it a bit. Here is the code for the Load, which isn't really interesting:
private static Act Load(string file)
{
SavedAction action;
BinaryFormatter bf = new BinaryFormatter();
using (Stream s = File.OpenRead(file))
action = (SavedAction)bf.Deserialize(s);
return (Act)Delegate.CreateDelegate(typeof(Act), action.target,
action.target.GetType().GetMethod(action.method));
}
The Save() is where the real magic begins, I compile the expression, extract the target, extract the method that was about to call, and save it, for later processing in the load.
private static void Save(Expression<Act> actionToSave)
{
Act act = actionToSave.Compile();
ExecutionScope scope = (ExecutionScope)act.Target;
SavedAction action = new SavedAction();
MethodCallExpression l = (MethodCallExpression)actionToSave.Body;
action.target = Expression.Lambda(l.Object).Compile().DynamicInvoke();
action.method = l.Method.Name;
BinaryFormatter bf = new BinaryFormatter();
using(Stream s = File.Create("Temp.action"))
bf.Serialize(s, action);
}
I am very excited about these capabilities. Yes, I can do it today, but the inteface I would have to expose is wholly unatural, while Linq provide for much nicer alternative.