Why can't I use lambda expressions while debugging in “Quick watch” window?
UPD: see also
Link
Link
No you cannot use lambda expressions in the watch / locals / immediate window. As Marc has pointed out this is incredibly complex. I wanted to dive a bit further into the topic though.
What most people don't consider with executing an anonymous function in the debugger is that it does not occur in a vaccuum. The very act of defining and running an anonymous function changes the underlying structure of the code base. Changing the code, in general, and in particular from the immediate window, is a very difficult task.
Consider the following code.
void Example() {
var v1 = 42;
var v2 = 56;
Func<int> func1 = () => v1;
System.Diagnostics.Debugger.Break();
var v3 = v1 + v2;
}
This particular code creates a single closure to capture the value v1. Closure capture is required whenever an anonymous function uses a variable declared outside it's scope. For all intents and purposes v1 no longer exists in this function. The last line actually looks more like the following
var v3 = closure1.v1 + v2;
If the function Example is run in the debugger it will stop at the Break line. Now imagine if the user typed the following into the watch window
(Func<int>)(() => v2);
In order to properly execute this the debugger (or more appropriate the EE) would need to create a closure for variable v2. This is difficult but not impossible to do.
What really makes this a tough job for the EE though is that last line. How should that line now be executed? For all intents and purposes the anonymous function deleted the v2 variable and replaced it with closure2.v2. So the last line of code really now needs to read
var v3 = closure1.v1 + closure2.v2;
Yet to actually get this effect in code requires the EE to change the last line of code which is actually an ENC action. While this specific example is possible, a good portion of the scenarios are not.
What's even worse is executing that lambda expression shouldn't be creating a new closure. It should actually be appending data to the original closure. At this point you run straight on into the limitations ENC.
My small example unfortunately only scratches the surface of the problems we run into. I keep saying I'll write a full blog post on this subject and hopefully I'll have time this weekend.
Lambda expressions, like anonymous methods, are actually very complex beasts. Even if we rule out Expression (.NET 3.5), that still leaves a lot of complexity, not least being captured variables, which fundamentally re-structure the code that uses them (what you think of as variables become fields on compiler-generated classes), with a bit of smoke and mirrors.
As such, I'm not in the least surprised that you can't use them idly - there is a lot of compiler work (and type generation behind the scenes) that supports this magic.
You can't use lambda expressions in the Immediate or Watch windows.
You can however use System.Linq.Dynamic expressions, which take the form .Where("Id = #0", 2) - it doesn't have the full range of methods available in standard Linq, and doesn't have the full power of lambda expressions, but still, it's better than nothing!
The future has come!
Support for debugging lambda expressions has been added to Visual Studio 2015 (Preview at the time of writing).
Expression Evaluator had to be rewritten, so many features are missing: remote debugging ASP.NET, declaring variables in Immediate window, inspecting dynamic variables etc. Also lambda expressions that require calls to native functions aren't currently supported.
this might help:
Extended Immediate Window for Visual Studio (use Linq, Lambda Expr in Debugging)
http://extendedimmediatewin.codeplex.com/
http://dvuyka.spaces.live.com/blog/cns!305B02907E9BE19A!381.entry
All the best,
Patrick
Lambda expressions are not supported by the debugger's expression evaluator... which is hardly surprising since at compile time they are used to create methods (or Expression Trees) rather than expressions (take a look in Reflector with the display switched to .NET 2 to see them).
Plus of course they could form a closure, another whole layer of structure.
In VS 2015 you can do so now,this is one of the new feature they added.
If you still need to use Visual Studio 2013, you can actually write a loop, or lambda expression in the immediate window using also the package manager console window. In my case, I added a list at the top of the function:
private void RemoveRoleHierarchy()
{
#if DEBUG
var departments = _unitOfWork.DepartmentRepository.GetAll().ToList();
var roleHierarchies = _unitOfWork.RoleHierarchyRepository.GetAll().ToList();
#endif
try
{
//RoleHierarchy
foreach (SchoolBo.RoleHierarchy item in _listSoRoleHierarchy.Where(r => r.BusinessKeyMatched == false))
_unitOfWork.RoleHierarchyRepository.Remove(item.Id);
_unitOfWork.Save();
}
catch (Exception e)
{
Debug.WriteLine(e.ToString());
throw;
}
}
Where my GetAll() function is:
private DbSet<T> _dbSet;
public virtual IList<T> GetAll()
{
List<T> list;
IQueryable<T> dbQuery = _dbSet;
list = dbQuery
.ToList<T>();
return list;
}
Here I kept getting the following error, so I wanted to print out all the items in the various repositories:
InnerException {"The DELETE statement conflicted with the REFERENCE constraint \"FK_dbo.Department_dbo.RoleHierarchy_OranizationalRoleId\". The conflict occurred in database \"CC_Portal_SchoolObjectModel\", table \"dbo.Department\", column 'OranizationalRoleId'.\r\nThe statement has been terminated."} System.Exception {System.Data.SqlClient.SqlException}
Then, I find out how many records are in the department repository by executing this in the immediate window:
_unitOfWork.DepartmentRepository.GetAll().ToList().Count
Which returned 243.
So, if you execute the following in the package manager console, it prints out all the items:
PM> for($i = 0; $i -lt 243; $i++) { $a = $dte.Debugger.GetExpression("departments[$i].OrgagnizationalRoleId"); Write-Host $a.Value $i }
The author for the idea can be found here
To answer your question, here's the Visual Studio Program Manager's official explanation of why you can't do this. In short, because "it's really, really hard" to implement in VS. But the feature is currently in progress (as updated on Aug 2014).
Allow the evaluation of lambda expressions while debugging
Add your vote while you're there!
Related
I am having a difficult time understanding the TPL and I cannot find many clear articles on it. Most seem to use simplistic examples with lambda expressions.
I have a C# function
int[] PlayGames(int[][] boardToSearch, int numGamesToPlay) {…}
I want to make this threadable using the .NET 4.6 TPL in C#. I want to launch up to 8 of these functions at once, wait until they all finish, capture the results and move on.
I can’t seem to get the types right and it’s not working as expected.
Here’s what I’ve got so far:
Task<int[]> PlayGames(int[][] boardToSearch, int numGamesToPlay) {…code that takes a long time…}
private int FindBestMove(int[][] boardToSearch, int numGamesToPlay)
{
…
var taskList = new List<Task>();
taskList.Add(Task.Factory.StartNew(() => { return PlayGames(boardToSearch, numGamesToPlay); }));
taskList.Add(Task.Factory.StartNew(() => { return PlayGames(boardToSearch, numGamesToPlay); }));
// Tests
Task a = taskList.First();
var a1 = a.Result; // NOT ALLOWED!? Viewable in debugger, cannot access it.
var b = Task.FromResult(a);
var b1 = b.Result; // Works but cannot access child member Result. Debugger sees it, I can’t!?
Task.WaitAll(taskList.ToArray());
…
}
Here are my questions
How do I remove the lambda expression () => { return PlayGames(boardToSearch, numGamesToPlay); }? I want to use Func() somehow but I cannot for the life of me figure out how to say “Task.Factory.StartNew<int[]>(Func(PlayGames(boardToSearch, numGamesToPlay)))”.
Why do I need to use StartNew()? When I do taskList.Add(PlayGames(int[][] boardToSearch, int numGamesToPlay)), it does it synchronously!? What is the correct syntax to add a task to a list in this manner? Do I need to declare a Func of some sorts that I pass to something like new Task(Func(PlayGames))?
When you look at variable a after executing the line Task a = taskList.First(), it clearly shows a member called Result in the debug pane. And if I expand that result it contains the right data! But if I click on add watch to the Result member, I get an error! And if I do a.Result, the compiler gives me the same error!?? So the debugger says it’s there but I cannot view it on its own!?? I can browse it from the a object but not directly. I included a screenshot of this so others could see.
What is the cleanest way to do this with .NET 4.6 while staying away from lambda expressions. I want to see all the types and the declarations.
Attached is a screenshot of my debugger so you can see what I mean with .Result
Let's start from the top:
1] Func it's just a delegate that's the part of .net framework libraries.
So, when you pass () => { return PlayGames(boardToSearch, numGamesToPlay); } it means you just create an anonymous method which has a type of Func<int[]>. If you assign this lambda expression to some variable then you can check this type.
If you don't want to use lambda you can write a common method and put it inside the task: Task.Factory.StartNew(YourMethodWhichReturnsIntArray).
2] When you call StartNew() method it just creates a new Task and starts execute this. That's it.
taskList.Add(PlayGames(int[][] boardToSearch, int numGamesToPlay)) - this just put the Task into the taskList. If inside your PlayGames method this Task wasn't started then you will need to do it sometime after. Synchronous or not - adding Task to list is synchronous operation, but executing still will be asynchronous. Any syntax might be correct or not - it depends on complexity and realization. Instead of Task.Factory.StartNew(), you can you a just Task.Run() method. It does the same, but in a bit shorten manner. And it's not necessary to declare a func before passing to the Task.
3] I believe is that's because the debugger has an ability to wait for the results from a parallel thread/task, but watcher doesn't. That's why you get an error.
So, I would say do not try to add watcher for the parallel threads results (just to skip the possible errors).
4] What is the cleanest way to do this with .NET 4.6 while staying away from lambda expressions. I want to see all the types and the declarations.
As I said above, it's not necessary to declare the lambda. You can create a method with the correspond definition. But here you will face some difficulties with passing the parameters to this method (but they still could be solved). So, lambda - is the easiest way to pass function to the Task, because it can easily capture you parameters from the scope where these lambda's have been created.
What is the cleanest way - again, it depends. Each of us has his own cleanest way to run new tasks. So, I think that (see below):
// your function
int[] PlayGames(int[][] boardToSearch, int numGamesToPlay) {…}
private int YouMethodToRun8Tasks(int[][] boardToSearch, int numGamesToPlay)
{
...
var taskList = new List<Task<int[]>>();
// create and run 8 tasks
for(var i = 0; i < 8; i++)
{
// it will capture the parameters and use them in all 8 tasks
taskList.Add(Task.Run<int[]>(() => PlayGames(boardToSearch, numGamesToPlay));
}
// do something else
...
// wait for all tasks
Task.WaitAll(taskList.ToArray());
// do something else
}
might be named as cleanest way in some cases.
I hope it will help you.
Do you feel question is strange? yes what happened also strange. let me explain.
I have found a snippet from this Covariance and Contravariance with C# Arrays
string[] strings = new string[1];
object[] objects = strings;
objects[0] = new object();
Jon skeet explains that above code will throw ArrayTypeMismatchException, as said yes it does.
what I did is I put a breakpoint in line 3, Using DebuggerVisualizer I manually set objects[0] = new object() it doesn't throw any error and it works. later checking strings[0].GetType() returns System.Object. not only System.Object any type can be set in string[] by above mentioned procedure.
I have no idea how this happened i raised my question as a comment over there in the very same question i saw this but no answers.
Am curious to know what is happening behind. Anybody explain pls.
Edit1 This is even Interesting
After reproducing the above behaviour try this
int len = strings[0].Length;
if you place mouse over the Property Length is says strings[0].Length threw ArgumentException with message Cannot find the method on the object instance but actually it doesnt throw exception and code runs yielding result len=0
Your example seems to answer the question: yes, a string reference can refer a non-string object. This is not intended, however.
Consider what you have found, a bug in the debugger.
As Jon Skeet explains in the answer you mention, because .NET arrays have this "crazy" covaraiance even though arrays are not read-only but more like read-write, everytime one writes to an array of references the framework has to check the type of the object one tries to write to the array, and throw an ArrayTypeMismatchException if you're about to use a wrong type, like assigning an instance of Cat to an array of Dogs (a runtime Dog[]) which has been cast by "crazy" covariance into an Animal[].
What you have demonstrated is that when we use the Immediate window of the Visual Studio debugger (or similar windows), this required type check is not done, and as a result this can lead to any type Y (except pointer types probably) being assigned to a reference type variable of any reference type X. Like this:
X[] arrayOfX = new X[1];
object[] arrayCastByCrazyCovariance = arrayOfX;
Y badObject = new Y(); // or another constructor or method to get a Y
// Set breakpoint here.
// In Immediate window assign: arrayCastByCrazyCovariance[0] = badObject
// Detach debugger again.
X anomalousReferenceVariable = arrayOfX[0];
anomalousReferenceVariable.MemberOfX(); // or other bad things
This can make a Cat bark like a Dog, and stuff like that.
In the linked thread on Bypassing type safeguards, the answer by CodesInChaos shows an unrelated technique with which you can put a reference to an object of a "wrong" and unrelated type into a reference variable.
(I have preferred to rewrite my answer because the previous one had too many updates and wasn't clear enough).
Apparently, it has been found a not-so-perfect behaviour in one of the tools (Immediate Window) in the VS debugging part. This behaviour does not affect (AT ALL) the normal execution of the code and, purely speaking, does not affect even the debugging process.
What I meant in the last sentence above is that, when I debug code, I never use the Immediate Window, just write any code I want, execute it and see what the debugger shows. The referred problem does not affect this process (which can be called "debugging actually-executed code"; in the proposed example, pressing F11 when you are on objects[0] = new object();), what would imply a serious problem in VS. Thus from my point of view (the kind of debugging I do) and from the execution point of view, the referred error has no effect at all.
The only application of this error is when executing the "Immediate Window" functionality, a feature of the debugger which estimates what the code will deliver before it actually delivers it (what might be called "debugging not-executed code" or "estimating expected outputs from non-executed code", etc.; in the proposed example, being on line objects[0] = new object();, not pressing F11 but using the Immediate Window to input values and let this feature to tell you what is expected to happen).
In summary, the referred problem has to be understood within the right context, that is, it is not an overall-applicable problem, not even a problem in the whole debugger (when you press F11 in the referred line from the debugger, it outputs an error and thus the debugger does understand perfectly that this situation is wrong), but just in one of its tools. I am not even sure if this behaviour is even acceptable for this tool (i.e., what "Immediate Window" delivers is a prediction which might not be 100% right; if you want to know for sure what will happen, execute the code and let the debugger show you the information).
QUESTION: Can a String[] hold System.Object inside it?
ANSWER: NO.
CLARIFICATION: covariance is a complex reality which might not be perfectly accounted by some of the secondary tools in VS (e.g.,
"Immediate Window") and thus there might be cases where the
aforementioned statement does not fully apply. BUT this is a local
behaviour/bug in the specific tool with no effect in the actual
execution of the code.
I have the following code inside a method:
string username = (string)context["UserName"];
string un = (string)context["UserName"];
The problem is that the first string "username" is not assigned, while the second does.
To make it more strange, when I have stopped the debugging after the first line and copied the line to the Immediate window, dropping the varible type delaration, it was assigned succesfully.
I have made rebuild all and checked project properties which seems to be OK.
The context variable is a System.Configuration.SettingsContext, which is a hash table. To be more specific, I'm implementing a profile provider, the GetPropertyValues method.
I am using VS 2012 and .NET 4.5
EDIT:
I am using code contract in my project, which uses compile time code injection for runtime checking. I disabled it and all is working well. I'll try to remove contracts one by one to find which one is causing the problem.
What you are seeing is similar to a Code Contracts bug I saw before. I wrote something about it here a few months back. If you have this bug, you probably also have a lambda or LINQ expression in your method that uses username.
For future reference, this is the bug I saw:
I had in the same method a lambda expression capturing a local variable, lets say values, and a Contract.Requires() expression checking something completely unrelated. While debugging that method, the debugger shows in Locals the variable values twice, and reports the value of values always as null even when this is clearly not the case.
To reproduce:
static void Reproduction(string argument)
{
Contract.Requires(argument != null); // <-- (1)
int[] values = new int[1];
Debug.Assert(values != null);
Func<int, bool> d = i => values[i] >= 0; // <-- (2)
Console.WriteLine(values);
}
Put a breakpoint somewhere in this method after the assignment to values and ensure that the method gets called. When the debugger hits the breakpoint, look at the Locals list of Visual Studio for the duplicate variable. Hover the mouse over values to find that it gets reported as being null, something which is clearly not the case.
The problem disappears when removing either the contract (1) or the line with the lambda (2).
After investigating and desabling contracts I found that the problem appears only when runtime contracts checking is enabled and this contract appears:
Contract.Ensures(Contract.Result<System.Configuration.SettingsPropertyValueCollection>() != null);
If I delete this line, the code works, so it looks like code contracts bug, though I couldn't recreate it on a test project.
First, some background info:
I am making a compiler for a school project. It is already working, and I'm expending a lot of effort to bug fix and/or optimize it. I've recently run into a problem with is that I discovered that the ILGenerator object generates an extra leave instruction when you call any of the following member methods:
BeginCatchBlock()
BeginExceptFilterBlock()
BeginFaultBlock()
BeginFinallyBlock()
EndExceptionBlock()
So, you start a try statement with a call to BeginExceptionBlock(), add a couple of catch clauses with BeginCatchBlock(), possibly add a finally clause with BeginFinallyBlock(), and then end the protected code region with EndExceptionBlock().
The methods I listed automatically generate a leave instruction branching to the first instruction after the try statement. I don't want these, for two reasons. One, because it always generates an unoptimized leave instruction, rather than a leave.s instruction, even when it's branching just two bytes away. And two, because you can't control where the leave instruction goes.
So, if you wanted to branch to some other location in your code, you have to add a compiler-generated local variable, set it depending on where you want to go inside of the try statement, let EndExceptionBlock() auto-generate the leave instruction, and then generate a switch statement below the try block. OR, you could just emit a leave or leave.s instruction yourself, before calling one of the previous methods, resulting in an ugly and unreachable extra 5 bytes, like so:
L_00ca: leave.s L_00e5
L_00cc: leave L_00d1
Both of these options are unacceptable to me. Is there any way to either prevent the automatic generation of leave instructions, or else any other way to specify protected regions rather than using these methods (which are extremely annoying and practically undocumented)?
EDIT
Note: the C# compiler itself does this, so it's not as if there is a good reason to force it on us. For example, if you have .NET 4.5 beta, disassemble the following code and check their implementation: (exception block added internally)
public static async Task<bool> TestAsync(int ms)
{
var local = ms / 1000;
Console.WriteLine("In async call, before await " + local.ToString() + "-second delay.");
await System.Threading.Tasks.Task.Delay(ms);
Console.WriteLine("In async call, after await " + local.ToString() + "-second delay.");
Console.WriteLine();
Console.WriteLine("Press any key to continue.");
Console.ReadKey(false);
return true;
}
As far as I can tell, you cannot do this in .NET 4.0. The only way to create a method body without using ILGenerator is by using MethodBuilder.CreateMethodBody, but that does not allow you to set exception handling info. And ILGenerator forces the leave instruction you're asking about.
However, if .NET 4.5 is an option for you (it seems to be), take a look at MethodBuilder.SetMethodBody. This allows you to create the IL yourself, but still pass through exception handling information. You can wrap this in a custom ILGenerator-like class of your own, with Emit methods taking an OpCode argument, and reading OpCode.Size and OpCode.Value to get the corresponding bytes.
And of course there's always Mono.Cecil, but that probably requires more extensive changes to code you've already written.
Edit: you appear to have already figured this out yourself, but you left this question open. You can post answers to your own questions and accept them, if you've figured it out on your own. This would have let me know I shouldn't have wasted time searching, and which would have let other people with the same question know what to do.
I'm testing performance differences using various lambda expression syntaxes. If I have a simple method:
public IEnumerable<Item> GetItems(int point)
{
return this.items.Where(i => i.IsApplicableFor(point));
}
then there's some variable lifting going on here related to point parameter because it's a free variable from lambda's perspective. If I would call this method a million times, would it be better to keep it as it is or change it in any way to improve its performance?
What options do I have and which ones are actually feasible? As I understand it is I have to get rid of free variables so compiler won't have to create closure class and instantiate it on every call to this method. This instantiation usually takes significant amount of time compared to non-closure versions.
The thing is I would like to come up with some sort of lambda writing guidelines that would generally work, because it seems I'm wasting some time every time I write a heavily hit lambda expression. I have to manually test it to make sure it will work, because I don't know what rules to follow.
Alternative method
& example console application code
I've also written a different version of the same method that doesn't need any variable lifting (at least I think it doesn't, but you guys who understand this let me know if that's the case):
public IEnumerable<Item> GetItems(int point)
{
Func<int, Func<Item, bool>> buildPredicate = p => i => i.IsApplicableFor(p);
return this.items.Where(buildPredicate(point));
}
Check out Gist here. Just create a console application and copy the whole code into Program.cs file inside namespace block. You will see that the second example is much much slower even though it doesn't use free variables.
A contradictory example
The reason why I would like to construct some lambda best usage guidelines is that I've met this problem before and to my surprise that one turned out to be working faster when a predicate builder lambda expression was used.
Now explain that then. I'm completely lost here because it may as well turn out I won't be using lambdas at all when I know I have some heavy use method in my code. But I would like to avoid such situation and get to the bottom of it all.
Edit
Your suggestions don't seem to work
I've tried implementing a custom lookup class that internally works similar to what compiler does with a free variable lambda. But instead of having a closure class I've implemented instance members that simulate a similar scenario. This is the code:
private int Point { get; set; }
private bool IsItemValid(Item item)
{
return item.IsApplicableFor(this.Point);
}
public IEnumerable<TItem> GetItems(int point)
{
this.Point = point;
return this.items.Where(this.IsItemValid);
}
Interestingly enough this works just as slow as the slow version. I don't know why, but it seems to do nothing else than the fast one. It reuses the same functionality because these additional members are part of the same object instance. Anyway. I'm now extremely confused!
I've updated Gist source with this latest addition, so you can test for yourself.
What makes you think that the second version doesn't require any variable lifting? You're defining the Func with a Lambda expression, and that's going to require the same bits of compiler trickery that the first version requires.
Furthermore, you're creating a Func that returns a Func, which bends my brain a little bit and will almost certainly require re-evaluation with each call.
I would suggest that you compile this in release mode and then use ILDASM to examine the generated IL. That should give you some insight into what code is generated.
Another test that you should run, which will give you more insight, is to make the predicate call a separate function that uses a variable at class scope. Something like:
private DateTime dayToCompare;
private bool LocalIsDayWithinRange(TItem i)
{
return i.IsDayWithinRange(dayToCompare);
}
public override IEnumerable<TItem> GetDayData(DateTime day)
{
dayToCompare = day;
return this.items.Where(i => LocalIsDayWithinRange(i));
}
That will tell you if hoisting the day variable is actually costing you anything.
Yes, this requires more code and I wouldn't suggest that you use it. As you pointed out in your response to a previous answer that suggested something similar, this creates what amounts to a closure using local variables. The point is that either you or the compiler has to do something like this in order to make things work. Beyond writing the pure iterative solution, there is no magic you can perform that will prevent the compiler from having to do this.
My point here is that "creating the closure" in my case is a simple variable assignment. If this is significantly faster than your version with the Lambda expression, then you know that there is some inefficiency in the code that the compiler creates for the closure.
I'm not sure where you're getting your information about having to eliminate the free variables, and the cost of the closure. Can you give me some references?
Your second method runs 8 times slower than the first for me. As #DanBryant says in comments, this is to do with constructing and calling the delegate inside the method - not do do with variable lifting.
Your question is confusing as it reads to me like you expected the second sample to be faster than the first. I also read it as the first is somehow unacceptably slow due to 'variable lifting'. The second sample still has a free variable (point) but it adds additional overhead - I don't understand why you'd think it removes the free variable.
As the code you have posted confirms, the first sample above (using a simple inline predicate) performs jsut 10% slower than a simple for loop - from your code:
foreach (TItem item in this.items)
{
if (item.IsDayWithinRange(day))
{
yield return item;
}
}
So, in summary:
The for loop is the simplest approach and is "best case".
The inline predicate is slightly slower, due to some additional overhead.
Constructing and calling a Func that returns Func within each iteration is significantly slower than either.
I don't think any of this is surprising. The 'guideline' is to use an inline predicate - if it performs poorly, simplify by moving to a straight loop.
I profiled your benchmark for you and determined many things:
First of all, it spends half its time on the line return this.GetDayData(day).ToList(); calling ToList. If you remove that and instead manually iterate over the results, you can measure relative the differences in the methods.
Second, because IterationCount = 1000000 and RangeCount = 1, you are timing the initialization of the different methods rather than the amount of time it takes to execute them. This means your execution profile is dominated by creating the iterators, escaping variable records, and delegates, plus the hundreds of subsequent gen0 garbage collections that result from creating all that garbage.
Third, the "slow" method is really slow on x86, but about as fast as the "fast" method on x64. I believe this is due to how the different JITters create delegates. If you discount the delegate creation from the results, the "fast" and "slow" methods are identical in speed.
Fourth, if you actually invoke the iterators a significant number of times (on my computer, targetting x64, with RangeCount = 8), "slow" is actually faster than "foreach" and "fast" is faster than all of them.
In conclusion, the "lifting" aspect is negligible. Testing on my laptop shows that capturing a variable like you do requires an extra 10ns every time the lambda gets created (not every time it is invoked), and that includes the extra GC overhead. Furthermore, while creating the iterator in your "foreach" method is somewhat faster than creating the lambdas, actually invoking that iterator is slower than invoking the lambdas.
If the few extra nanoseconds required to create delegates is too much for your application, consider caching them. If you require parameters to those delegates (i.e. closures), consider creating your own closure classes such that you can create them once and then just change the properties when you need to reuse their delegates. Here's an example:
public class SuperFastLinqRangeLookup<TItem> : RangeLookupBase<TItem>
where TItem : RangeItem
{
public SuperFastLinqRangeLookup(DateTime start, DateTime end, IEnumerable<TItem> items)
: base(start, end, items)
{
// create delegate only once
predicate = i => i.IsDayWithinRange(day);
}
DateTime day;
Func<TItem, bool> predicate;
public override IEnumerable<TItem> GetDayData(DateTime day)
{
this.day = day; // set captured day to correct value
return this.items.Where(predicate);
}
}
When a LINQ expression that uses deferred execution executes within the same scope that encloses the free variables it references, the compiler should detect that and not create a closure over the lambda, because it's not needed.
The way to verify that would be by testing it using something like this:
public class Test
{
public static void ExecuteLambdaInScope()
{
// here, the lambda executes only within the scope
// of the referenced variable 'add'
var items = Enumerable.Range(0, 100000).ToArray();
int add = 10; // free variable referenced from lambda
Func<int,int> f = x => x + add;
// measure how long this takes:
var array = items.Select( f ).ToArray();
}
static Func<int,int> GetExpression()
{
int add = 10;
return x => x + add; // this needs a closure
}
static void ExecuteLambdaOutOfScope()
{
// here, the lambda executes outside the scope
// of the referenced variable 'add'
Func<int,int> f = GetExpression();
var items = Enumerable.Range(0, 100000).ToArray();
// measure how long this takes:
var array = items.Select( f ).ToArray();
}
}