If I’m going to suggest that others use yield return
, I suppose I should take my own advice. In particular, my original implementation of SPWebCollection.Select can be improved using the same techniques I used for Where:
public static IEnumerable<TResult> Select<TResult>(this SPWebCollection webs, Func<SPWeb, TResult> selector) { foreach (SPWeb web in webs) { TResult ret = selector(web); web.Dispose(); yield return ret; } }
And we might as well support a filtered Select as well:
public static IEnumerable<TResult> Select<TResult>(this SPWebCollection webs, Func<SPWeb, TResult> selector, Func<SPWeb, bool> predicate) { foreach (SPWeb web in webs.Where(predicate)) { TResult ret = selector(web); web.Dispose(); yield return ret; } }
Implementations using Cast<T>() are left as an exercise for the reader.
The function itself isn’t particularly interesting, but I did stumble on something I found rather surprising. When I first wrote up my function, I typed the following:
public static IEnumerable<TResult> Select<TResult>(this SPWebCollection webs, Func<SPWeb, TResult> selector) { foreach (SPWeb web in webs) { yield return selector(web); web.Dispose(); } }
I’m really not sure why I typed it that way…obviously you can’t keep going after you return, right? Well it turns out you can. The generated class just waits to call Dispose() until the next call to MoveNext(), effectively picking up where it left off. Here’s what that looks like in Reflector:
switch (this.<>1__state) { case 0: this.<>1__state = -1; this.<>7__wrap1a = this.webs.GetEnumerator(); this.<>1__state = 1; while (this.<>7__wrap1a.MoveNext()) { this.<web>5__19 = (SPWeb) this.<>7__wrap1a.Current; this.<>2__current = this.selector(this.<web>5__19); this.<>1__state = 2; return true; Label_0080: this.<>1__state = 1; this.<web>5__19.Dispose(); } this.<>m__Finally1c(); break; case 2: goto Label_0080; } return false;
As Select will almost always be used in a foreach, with back-to-back calls of MoveNext(), the distinction is mostly academic. Still, I prefer to know that the web will be disposed immediately after the selector is done with it.
December 11, 2008 at 1:27 am
[…] 12/10/2008: New, improved Select! Discussed here. Posted in Object Model, SharePoint. Tags: anonymous type, dispose, extension method, […]
January 5, 2009 at 10:48 pm
[…] Revisited: AsSafeEnumerable January 5, 2009 — Keith Dahlby I have previously discussed implementations of some core LINQ extension methods for SPWebCollection, a process complicated by […]