What does JetBrains.Annotations.LinqTunnelAttribute do ?
Only 9 results in google, and the only documentation I can find about it is:
Indicates that method is *pure* linq method, with postponed enumeration. C# iterator methods (yield ...) are always LinqTunnel
So basically any method returning IEnumerable<T> with deferred enumeration should be marked with it ?
If so, what does R# do with this information ?
Also, isn't the whole point of IEnumerable<T> is that it's always deferred (thus making this attribute redundant)?
After all, if it's not, you should really be returning ICollection<>, IList<>, array, etc.
(there are some debatable tricks and excuses to return IEnumerable<T> even though an actual collection is returned - e.g. when you don't want to return an internal structure as modifiable, so maybe it caters to those cases?)
Thanks!
Please sign in to leave a comment.
"LinqTunnelAttribute" affects analisys of lambda/anonymous function passed to the function as argument. So, if the method returning IEnumerable<T> does not have function parameter then there is no sense to mark the method with LinqTunnelAttribute.
This attribute is used to understand if the lambda/anonymous argument is executed instantly (during the method execution) or can be executed later. Here is an example ("Where" method is marked with LinqTunnelAttribute):
public void Test(int[] ints)
{
var min = 0;
var max = 100;
var en1 = ints.Where(i => i > min); // "Access to modified closure" warning on "min" because ReSharper is not sure that the lambda is executed instantly
var en2 = ints.Where(i => i > min).Where(i => i < max).ToList(); // No warning because ReSharper know that the lambdas is executed instantly
min = 50;
max = 200;
}
And, as you pointed, not every method returning IEnumeable<T> is deferred, sometimes developer just want to return non-modifiable collection, so we cannot automatically apply this attribute to every method returning IEnumerable<T>
I see this post got resurrected somehow, so I'm reposting here (mods - feel free to delete this post)
Hi Ivan,
Thanks for the explanation. However, using your code, switching Where() with MyWehere() defined as:
public static IEnumerable<T> MyWhere<T>(this IEnumerable<T> collection, Func<T,bool> predicate)
{
return collection.Where(predicate);
}
I get the same result ("Access to modified closure"), even though MyWhere is not annotated with LinqTunnelAttribute. So if that's the default assumption R# makes I'm not sure what is there to gain with this attribute.
Thanks again
Hi,
Try to replace "Where" to "MyWhere" in the second line of my sample:
var en2 = ints.MyWhere(i => i > min).MyWhere(i => i < max).ToList();
and you will see warnings on "min" and "max" about access to modified closure.
The point here is that ReSharper looks to the next method in the chain when sees method annotated with "LinqTunnel" attribute. In the first line therer is no next method so ReSharper suppose that lambda execution can be deffered. In the second line there is method "ToList" which ReSharper knows is executed immediately.
Hi Ivan,
I think I understand now.
Basically resharper identifies LINQ "chains" that consist of methods decorated with LinqTunnel attributes and end with an evaluating method (Count, Any, ToList, ToArry, etc).
For such "chains" it knows all the expressions throughout will be evaluated immediately.
However when it encounters a method that's not decorated with the LinqTunnel attribute the chain is broken as it can't be sure the lambda isn't kept for later execution, and so the "access to modified clousre" warning is displayed.
Did I get it right ?
Yes, that's right
Thanks Ivan,
Any chance you could take a look at My other question pertaining to ValueFlowAttribute ?
http://devnet.jetbrains.com/thread/454937?tstart=0