Is there a way to tell ReSharper that my method returns a non-deferred IEnumerable?

Hi,

I use a lot of IEnumerable<T> objects in my code base, so I'm plagued with the "Possible multiple enumeration of IEnumerable" warnings from the inspector. I could switch off this warning but I can see it could be quite useful.

I've knocked up a simple extension method to ensure that any given IEnumerable<T> is a non-deferred list:

 public static IEnumerable<T> ToNonDeferred<T>(this IEnumerable<T> list)

 {

  if ((list is T[]) || (list is IList<T>) || (list is ICollection<T>))

  {

    return list;

  }

  return list.ToArray();

 }

...and I use this on methods that take IEnumerables as parameters:

void ShowList<T>(IEnumerable<T> data)

{

  var list = data.ToNonDeferred();

  grid.Visible = list.Any();

  grid.Bind(list);

}


(Note: I've just knocked up this example to illustrate the point, so I'm not looking for a workaround for this specific example.  I know that the code could be better written :) )

Whilst the extension methods ensures that multiple enumerations are not an issue, I still get warnings from the inspector.

So... I'm wondering if there is a way of indicating to the inspection engine that a given return value is a non-deferred IEnumerable.  Something like:

[return:JetBrains.Annotations.EnumerationIsNonDeferred]

public static IEnumerable<T> ToNonDeferred<T>(this IEnumerable<T> list)

{}




6 comments
Comment actions Permalink

ReSharper doesn't offer any attributes to say that an IEnumerable can be enumerated more than once. Instead, if your method requires an enumerable that can be used more than once, it should use a parameter with a type that indicates this, such as IList, or convert the given enumerable with .ToList(). If you change your extension method to return an IList, then ReSharper won't issue the warnings any more.

The only annotations ReSharper offers are InstantHandleAttribute and NoEnumerationAttribute. InstantHandle means passing an enumerable is enumerated immediately inside the method, and NoEnumeration simply means the enumerable isn't enumerated in the called method (http://devnet.jetbrains.com/thread/305606).

There is a YouTrack issue to track adding something like (No)InstantHandle on return methods, as you've suggested - I'd encourage you to vote on the issue: http://youtrack.jetbrains.com/issue/RSRP-288331

Thanks
Matt

0
Comment actions Permalink

Thanks Matt,

I've voted for the feature.

0
Comment actions Permalink

The simple solution for this method:

public static ICollection<T> ToNonDeferred<T>(this IEnumerable<T> list)
{
   return list as ICollection<T> ?? list.ToArray();
}


This will cover all of the existing cases, since T[] implements IList<T>, and IList<T> implements ICollection<T>.

0
Comment actions Permalink

I'd prefer to return IEnumerable<T> as this keeps the purity of the method, and avoids the implication that the array can be mutated:

public static IEnumerable<T> ToNonDeferred<T>(this IEnumerable<T> list)
{
   return list as ICollection<T> ?? list.ToArray();
}


...although I appreciate your point that using ICollection<T> would remove the "possible multiple enumeration..." warnings. :-)
0
Comment actions Permalink

If you're using .NET 4.5, you could return IReadOnlyCollection<T>:

public static IReadOnlyCollection<T> ToNonDeferred<T>(this IEnumerable<T> list)
{
   return list as IReadOnlyCollection<T> ?? list.ToArray();
}


However, since ICollection<T> doesn't implement IReadOnlyCollection<T>, this could result in more collections being copied to arrays.

0
Comment actions Permalink

That's an interesting one.  I've not come across that interface before...

I'll have a think about this, as it is a more appropriate fit than returning ICollection.  I suspect, for my code base at least, there would be little difference between this and using .ToArray() directly. :-)

0

Please sign in to leave a comment.