LinqPossibilities
Jeff Brown commented on my Linq Options post:
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.
More posts in "Linq" series:
- (14 Apr 2007) Functions
- (05 Apr 2007) More Implementation Details
- (20 Mar 2007) Orderring and Paging
Comments
Hi,
This is incredibly clever !
I see a lot of opportunities with such constructs.
Linq is going to change the way we code in C#.
By the way, your work on Linq for NHibernate doesn't go unnoticed. Excitement is growing about your work as an alternative to Linq + the MS OR/M.
Here is a link from a very regarded French blog:
http://www.dotnetguru.org/modules.php?op=modload&name=News&file=article&sid=907
Thanks.
I really was just referring to control flow in the example you had provided. Expression trees do expose a lot more interesting internal structure which makes things like Linq possible. I'm looking forward to using them for MbUnit v3.
You example is cute but I don't think it's a good use of lambda expressions since it makes too many assumptions about the form the expression provided. What if I handed it something that did not include a method call? The syntax may be convenient but the representation makes it unclear what behavior is anticipated. I'll grant that this has a lot to do with the simplicity of the Load() and Save() methods presented for the purposes of the example.
At the same time, I'm irritated that lambda expressions are not really usable for user-defined control structures, they are not serializable, and they cannot be used as constant expressions in attribute declarations. It would be nice if C# provided more closure in terms of its language contructs. It's like the using statement in that way. Lots of potential but ultimately very limiting.
Pet peeve: I can use numeric and string constants in switch statements but not other value types even though the compiler does generate a hashtable based dispatch mechanism for strings. Is it trying to protect me from myself?
Comment preview