Traversing a list, execute a method: Extension possible?
i have a data structure like this
public class Employee
{
public string Name { get; set; }
public IEnumerable<Employee> Employees { get; private set; }
// ...开发者_StackOverflow社区
}
Now i need to loop through the complete structure and execute a method on each item.
How can i create an extension on IEnumerable for such a traverse function.
Wonderfull would be something like this
employeList.Traverse(e => Save(e), e.Employees.Count > 0);
Or is it impossible and i have to create a special method in my business logic?
Thanks a lot.
Do you mean an extension method on IEnumerable<Employee>
? That's certainly feasible:
public static void Traverse(this IEnumerable<Employee> employees,
Action<Employee> action,
Func<Employee, bool> predicate)
{
foreach (Employee employee in employees)
{
action(employee);
// Recurse down to each employee's employees, etc.
employee.Employees.Traverse(action, predicate);
}
}
This has to be in a static, non-generic, non-nested class.
I'm not sure what the predicate bit is for, mind you...
EDIT: Here's the more generalised form I think you were looking for:
public static void Traverse<T>(this IEnumerable<T> items,
Action<T> action,
Func<T, IEnumerable<T>> childrenProvider)
{
foreach (T item in items)
{
action(item);
Traverse<T>(childrenProvider(item), action, childrenProvider);
}
}
You'd then call it with:
employees.Traverse(e => Save(e), e => e.Employees);
I'm assuming your main class should be Employer
rather than Employee
.
public static class EmployerExtensions
{
public static void Traverse(this Employer employer, Action<Employee> action)
{
// check employer and action for null and throw if they are
foreach (var employee in employer.Employees)
{
action(employee);
}
}
}
I'm not sure what your parameters are supposed to signify, but if the second parameter is a predicate, you may want to do something like this:
public static void Traverse(this IEnumerable<T> source, Action<T> action, Func<T,bool> predicate) {
foreach(T item in source.Where(predicate)) {
action.Invoke(item);
}
}
I might also throw in that there already is such a function, on List<T>
, so if ToList
is not an issue, you would be able to do
employeList.Where(e => e.Employees.Count > 0).ToList().ForEach(Save);
You can do it with a simple extension method:
employeeList.ForEach(e => Save(e));
public static partial class IEnumerableExtensions
{
/// <summary>
/// Executes an <see cref="Action<T>"/> on each item in a sequence.
/// </summary>
/// <typeparam name="T">The type of the elements of <paramref name="source"/>.</typeparam>
/// <param name="source">An <see cref="IEnumerable<T>"/> in which each item should be processed.</param>
/// <param name="action">The <see cref="Action<T>"/> to be performed on each item in the sequence.</param>
public static void ForEach<T>(
this IEnumerable<T> source,
Action<T> action
)
{
if (source == null)
throw new ArgumentNullException("source");
if (action == null)
throw new ArgumentNullException("action");
foreach (T item in source)
action(item);
}
}
While passing an Action might be useful, it isn't as flexible as an iterator that enumerates all items in a tree structure, making them available for use with other LINQ operators:
public static class ExtensionMethods
{
// Enumerate all descendants of the argument,
// but not the argument itself:
public static IEnumerable<T> Traverse<T>( this T item,
Func<T, IEnumerable<T>> selector )
{
return Traverse<T>( selector( item ), selector );
}
// Enumerate each item in the argument and all descendants:
public static IEnumerable<T> Traverse<T>( this IEnumerable<T> items,
Func<T, IEnumerable<T>> selector )
{
if( items != null )
{
foreach( T item in items )
{
yield return item;
foreach( T child in Traverse<T>( selector( item ), selector ) )
yield return child;
}
}
}
}
// Example using System.Windows.Forms.TreeNode:
TreeNode root = myTreeView.Nodes[0];
foreach( string text in root.Traverse( n => n.Nodes ).Select( n => n.Text ) )
Console.WriteLine( text );
// Sometimes we also need to enumerate parent nodes
//
// This method enumerates the items in any "implied"
// sequence, where each item can be used to deduce the
// next item in the sequence (items must be class types
// and the selector returns null to signal the end of
// the sequence):
public static IEnumerable<T> Walk<T>( this T start, Func<T, T> selector )
where T: class
{
return Walk<T>( start, true, selector )
}
// if withStart is true, the start argument is the
// first enumerated item in the sequence, otherwise
// the start argument item is not enumerated:
public static IEnumerable<T> Walk<T>( this T start,
bool withStart,
Func<T, T> selector )
where T: class
{
if( start == null )
throw new ArgumentNullException( "start" );
if( selector == null )
throw new ArgumentNullException( "selector" );
T item = withStart ? start : selector( start );
while( item != null )
{
yield return item;
item = selector( item );
}
}
// Example: Generate a "breadcrumb bar"-style string
// showing the path to the currently selected TreeNode
// e.g., "Parent > Child > Grandchild":
TreeNode node = myTreeView.SelectedNode;
var text = node.Walk( n => n.Parent ).Select( n => n.Text );
string breadcrumbText = string.Join( " > ", text.Reverse() );
精彩评论