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!

2
6 comments

"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>

1
Avatar
Permanently deleted user

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

0

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.

0
Avatar
Permanently deleted user

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 ?

2

Yes, that's right

0
Avatar
Permanently deleted user

Thanks Ivan,

Any chance you could take a look at My other question pertaining to ValueFlowAttribute ?
http://devnet.jetbrains.com/thread/454937?tstart=0 

0

Please sign in to leave a comment.