I have written a small custom Rosyln Analyzer that checks for certain method invocations, and if they were executed within a loop context or expression. An example of the code I am wanting to warn on can be found below:
IMessageHandlerContext context = default;
foreach (var item in dataItems)
{
await context.Send<IProcessItem>(m =>
{
m.Item = "my-item-id";
});
}
I perform a two-stage analysis by first checking the method invocation is one I am specifically interested in, by looking at the method name. The second change is to look at the wider context, and test if the Invocation happens inside a loop context or expression. The Analyzer works for For and ForEach constructs, but fails to highlight for the While loop expression. I am satisified the first stage, which looks at Method names, is working fine.
private void CheckForMethodInvocationInLoop(SyntaxNodeAnalysisContext context)
{
var invocation = (InvocationExpressionSyntax)context.Node;
var containsMethod = false;
// Removed stage one code for brevity
if (!containsMethod)
return;
var loopFound = invocation.FirstAncestorOrSelf<StatementSyntax>(ss =>
ss.IsKind(SyntaxKind.ForStatement) ||
ss.IsKind(SyntaxKind.ForEachStatement) ||
ss.IsKind(SyntaxKind.WhileStatement));
if (loopFound != null)
{
context.ReportDiagnostic(Diagnostic.Create(Rule, invocation.GetLocation()));
}
}
The Analyzer correctly highlights method invocations within For and ForEach expressions, however does not identify the same invocations in While expressions.
Invocation inside ForEach expression, syntax hightlighting
Invocation inside While expression, no syntax highlighting
I have tried a couple of techniques to check the invocation context. Firstly, by recursively checking each parent, and it's parent, on the invocation statement. I have also attempted to utilise the method FirstAncestorOrSelf on the invocation itself to test for a kind of Statement. By inspecting the Syntax Visualizer I cannot determine why the analyzer cannot identify the invocation inside the While expression.
So I took a look at your repo and after fixing a few quick things (like the analyzer not being referenced in your main project, or the fact that analyzers have to be build against netstandard2.0) the diagnostics worked as expected:
Related
I am using linq in a simple console application and I get this error and can't figure out what is wrong. Never got this error before with linq.
public class Program
{
public static List<string> names = new List<string>();
static void Main(string[] args)
{
names.Add("Ruben");
names.Add("Dirk");
names.Add("Jan");
names.Add("Klaas");
var test = names.SingleOrDefault(x => x.EndsWith("k"));
}
}
The x variable is only visible within the LINQ expression itself. So while the debugger is on the assignment statement var test = names.SingleOrDefault... you won't see it.
You can set a breakpoint on x.EndsWith("k") then you will see the x value when it is hit.
This is completely normal. It happens when predicate parameter is optimized out when code is compiled and not available to the debugger. When you continue to run the highlighted statement you should see the test variable is evaluated, if there're no other exceptions, which I'm referring to SingleOrDefault() call, it throws an exception when you have multiple results of the predicate test.
And also, as #klaus-gütter mentioned the variables in LINQ expressions are visible in the expression execution only, when they're not optimized out.
I need to call the method for each item in the list. So, I have used Where<> query as follows,
List<string> list = new List<string>();
list.Add("Name1");
list.Add("Name2");
list.Add("Name3");
var name = list.Where(n =>
{
return CheckName(n);
});
But in the above case, CheckName() is not hit. The same method is triggered if I use FirstOrDefault<>. I don't know whether it is a framework break or I am going in a wrong way.
As additional info, I am using.NET Framework 4.5.
Has anyone experienced this error? If so, is there any solution to overcome this issue?
You are understanding incorrectly the result of the Where condition. As linq is deffered executed it will only enter the where condition when materialized (by a ToList/FirstOrDefault/Sum and such).
The Where is never actually materialized in your current code (It did as you experienced when using FirstOrDefault) and as such it will never enter the CheckName method. Then, as Where will never return null but "worst case" an empty collection, which is not null, the result is true.
If you debug you will see that name equals true at the end of this. To "overcome" this depends on what is your desired output:
If you want to know if you have any item that matched the predicate then:
var result = list.Any(CheckName);
If you want to retrieve those that match the predicate:
var result = list.Where(CheckName);
If later you want to query and check if results contains anything then:
if(result.Any()) { /* ... */ }
If you only want the results (and thus materializing the query):
list.Where(CheckName).ToList();
Read more about linq being deffered executed here:
Linq and deffered execution
What are the benefits of a Deferred Execution in LINQ?
Just as a side note see how you can change your current code from:
var name = list.Where(n =>
{
return CheckName(n);
})
To:
var name = list.Where(n => CheckName(n));
And eventually to:
var name = list.Where(CheckName);
LINQ has a Deferred Execution principal which means the query will not be executed until and unless you access name variable. If you want to execute it immediately, (just for example) add .ToList() in the end, which is exactly what FirstOrDefault does. It does immediate execution instead of deferred execution.
var name = list.Where(n =>
{
return CheckName(n);
}).ToList() != null;
Also where condition result will never be null. Even if there is no object in list satisfying your condition(s) in CheckName, where will return an empty collection.
The CheckName() method is not executed because of Deferred execution of Linq. The actual statement is not executed till you actually access it. So in your case, for the CheckName(), you should do something like:
var name = list.Where(n =>
{
return CheckName(n);
}).ToList();
When you look at the Where-Method source code you can easily see why:
internal static IEnumerable<T> Where<T>(this IEnumerable<T> enumerable, Func<T, bool> where) {
foreach (T t in enumerable) {
if (where(t)) {
yield return t;
}
}
}
The yield will cause the execution to only happen once the returned IEnumerable<T> is actually accessed. That is what is called deferred execution.
If you need to call a method for each item in a list then you should use a simple for loop:
foreach var name in list
CheckName(name);
Just because LINQ is available, doesn't mean it should be used everywhere there is a collection. It is important to write code that makes sense and is self commenting and using it here has simultaneously introduced a flaw into your logic and made your code harder to read, understand and maintain. It's the wrong tool for the stated purpose
Doubtless you have additional requirements not stated here, like "I want to check every name in a list and make sure that none are null". You can and possibly should use linq for this but it looks more like
bool allNamesOK = list.All(n => n != null);
This code is compact and reads well; we can clearly see the intention (though I wouldn't call the list "list" - "names" would better)
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.
I was hoping someone could explain to me what bad thing could happen in this code, which causes ReSharper to give an 'Access to modified closure' warning:
bool result = true;
foreach (string key in keys.TakeWhile(key => result))
{
result = result && ContainsKey(key);
}
return result;
Even if the code above seems safe, what bad things could happen in other 'modified closure' instances? I often see this warning as a result of using LINQ queries, and I tend to ignore it because I don't know what could go wrong. ReSharper tries to fix the problem by making a second variable that seems pointless to me, e.g. it changes the foreach line above to:
bool result1 = result;
foreach (string key in keys.TakeWhile(key => result1))
Update: on a side note, apparently that whole chunk of code can be converted to the following statement, which causes no modified closure warnings:
return keys.Aggregate(
true,
(current, key) => current && ContainsKey(key)
);
In that particular code nothing, take the following as an example:
int myVal = 2;
var results = myDatabase.Table.Where(record => record.Value == myVal);
myVal = 3;
foreach( var myResult in results )
{
// TODO: stuff
}
It looks like result will return all records where the Value is 2 because that's what myVal was set to when you declared the query. However, due to deferred execution it will actually be all records where the Value is 3 because the query isn't executed until you iterate over it.
In your example the value isn't being modified, but Resharper is warning you that it could be and that deferred execution could cause you problems.
When you modify the result variable, the closure (the use of the variable inside the lambda expression) will pick up the change.
This frequently comes as an unexpected surprise to programmers who don't fully understand closures, so Resharper has a warning for it.
By making a separate result1 variable which is only used in the lambda expression, it will ignore any subsequent changes to the original result variable.
In your code, you're relying on the closure picking up the changes to the original variable so that it will know when to stop.
By the way, the simplest way to write your function without LINQ is like this:
foreach (string key in keys) {
if (ContainsKey(key))
return true;
}
return false;
Using LINQ, you can simply call Any():
return keys.Any<string>(ContainsKey);
See
http://ericlippert.com/2009/11/12/closing-over-the-loop-variable-considered-harmful-part-one/
for an extensive discussion of this issue and what we might do in hypothetical future versions of C# to mitigate it.
I'm not sure if ReSharper will give the exact same warning for this, but the following illustrates a similar situation. The iterator for a loop is used in a LINQ clause, but the clause isn't actually evaluated until after the loop has finished, by which time the iterator variable has changed. The following is a contrived example that looks like it should print all odd numbers from 1 to 100, but actually prints all numbers from 1 to 99.
var notEven = Enumerable.Range(1,100);
foreach (int i in Enumerable.Range(1, 50))
{
notEven = notEven.Where(s => s != i * 2);
}
Console.WriteLine(notEven.Count());
Console.WriteLine(string.Join(", ", notEven.Select(s => s.ToString()).ToArray()));
This can be quite an easy mistake to make. I've heard people say that if you truly understand closures/functional programming/etc. you should never make this mistake. I've also seen professional developers who certainly do have a good grasp of those concepts make this exact mistake.
Well, the warning is because result could be changed before the closure is executed (variables are taken at execution, not defition). In your case, you are actually taking advantage of that fact. If you use the resharper reccomodation, it will actually break your code, as key => result1 always returns true.
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!