This post was migrated from an older system. Some links may point to content that no longer exists. Comments were not migrated.
Awhile ago, I was conversing with someone about the features of the newer .Net Framework versions. One of the features mentioned was generics. My friend mentioned that he really liked the concept and recently started using them, but that he still couldn't quite see how generic collections could really be put to work. Ok... so that's the lead in for today's post - generics, collections, and 1 more "new" feature called anonymous methods.
For starters, f you haven't looked at generics, shame on you. That is some seriously wicked stuff that will do nothing but help you in the long run. If you haven't checked out generic collections, that's ok... I think a lot of folks haven't really touched on that area because so much legacy code relies on the old way of working with collections. Anyways, if you need some help with generics and generic collections, open up a new browser session and read up on the topics.
First, let's dive into how generic collections can be utilized...
Everyone's probably really familiar with writing a recursive algorithm. It's a dev 101 type of thing to learn and use. Some of the most common recursive methods are those used to find information that is buried in a tree of some sort (e.g. looking for a specific Control on your page, etc.). For example, a method to find a specific Control would look something like...
public Control FindControlOnPage (Control control, string targetID) {
Control current = control;
if (current.ID == targetID) {
return current;
}
foreach (Control c in current.Controls) {
Control found = FindControlOnPage (c, targetID);
if (found != null) {
return found;
}
}
return null;
}
Unfortunately, recursive algorithms can be hard to follow and/or debug. Especially if your algorithm is fairly complex or the tree is pretty large.
With generic collections, you have an opportunity to replace a traditional recursive method with a Stack collection. For example...
public Control FindControlUsingStack (Control rootControl, string targetID) {
Stack<Control> stack = new Stack<Control> ();
stack.Push(rootControl);
while (stack.Count != 0) {
Control current = stack.Pop ();
if (current.ID == targetID) {
return current;
}
foreach (Control c in current.Controls) {
stack.Push(c);
}
}
}
As you can see, generic collections make it possible to do things a little differently. It's not to say this is the best (or worst) way to search for a control. Nor does the example code account for errors or circular references. The best part of this methodology (at least for me) is the simple fact that the code has a very inline feel and the debugging experience is much kinder.
What about anonymous methods?
Delegates are nothing more than method signatures. In older versions of the .Net Framework, delegates could only be used in named methods. In the C# 2.0, you now have the ability to simply instantiate a delegate and specify a code block that the delegate will immediately process when it's called. A lot of people are probably familiar with using the
RunWithElevatedPrivileges method with an anonymous method as shown belowe (example taken from the MSDN reference page):
SPSecurity.RunWithElevatedPrivileges(delegate()
{
using (SPSite site = new SPSite(web.Site.ID))
{
// implementation details omitted
}
});
Anonymous methods are super handy... so let's roll all of this information into a handy SharePoint routine!
Let's setup a few design points for our scenario...
- Create a method that executes a method on all sub-sites of a SharePoint site but doesn't execute that method on the parent site
- It doesn't use traditional recursion
- It doesn't use the AllWebs property
To solve this problem,
- Define a delegate that takes a SPWebCollection object and
- Create stack-based "recursion" method.
Your library code will look like...
public class Utility {
/// <summary>
/// Delegate that takes an SPWeb object as its only parameter
/// </summary>
public delegate void CodeToExecute (SPWeb web);
private Utility () {
}
/// <summary>
/// Execute a method against all children of the specified web
/// </summary>
public static void ExecuteRecursively (SPWeb web, CodeToExecute code) {
Stack<SPWebCollection> stack = new Stack<SPWebCollection>();
stack.Push(web.Webs);
while (stack.Count != 0) {
foreach (SPWeb w in stack.Pop()) {
stack.Push(w.Webs);
code(w);
}
}
}
}
Your utility class is complete. Now it's time to use it!
The "ExecuteRecursively" method can be called using an anonymous method or the good old-fashioned way of a named method. Here's a call with an anonymous delegate...
Utility.ExecuteRecursively (this.Web, delegate(SPWeb w) {
if (w.HasUniqueRoleAssignments) {
w.RoleAssignments.Remove(w.CurrentUser);
}
} );
Note how you can easily reference the active SPWeb w in the anonymous method. The delegate definition allows you pass in a web, from which you can reference within your anonymous method.
Wth the utility class in hand, it's super easy to write a "recursive" method in practically no time flat. The key advantages to using this methodology is speed and, once again, ease of debugging.
Finally, keep in mind the ExecuteRecursively method is slightly different than the FindControlUsingStack method shown earlier. The utility class was designed to operate on the children of the starting point, not the starting point itself (like in the FindControlUsingStack example). Within the utility class, you can incorporate logic that best suites your needs (for example, perhaps you want to test if a SPWeb object is of a particular type before running the code) and starting points.
Cool, eh? Happy coding!
-Maurice