【问题标题】:Traversing a list, execute a method: Extension possible?遍历一个列表,执行一个方法:扩展可能吗?
【发布时间】:2011-03-17 22:18:18
【问题描述】:

我有这样的数据结构

public class Employee
{
    public string Name { get; set; }
    public IEnumerable<Employee> Employees { get; private set; }
    // ...
}

现在我需要遍历整个结构并对每个项目执行一个方法。

如何在 IEnumerable 上为这样的遍历函数创建扩展。

Wonderfull 会是这样的

employeList.Traverse(e => Save(e), e.Employees.Count > 0);

或者这是不可能的,我必须在我的业务逻辑中创建一个特殊的方法?

非常感谢。

【问题讨论】:

    标签: c# .net linq .net-3.5 lambda


    【解决方案1】:

    您是指IEnumerable&lt;Employee&gt; 上的扩展方法吗?这当然是可行的:

    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);
        }
    }
    

    这必须在静态、非泛型、非嵌套类中。

    我不确定谓词位的用途,请注意...

    编辑:这是我认为您正在寻找的更通用的形式:

    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);
        }
    }
    

    然后你会调用它:

    employees.Traverse(e => Save(e), e => e.Employees);
    

    【讨论】:

    • 是的,这就是我所做的。我尝试为每个 IEnumerable (Enumerable) 建立一个扩展。但我的问题是/是扩展中子项(employee.Employees.Traverse)的循环。我首先想到,我可以创建一个扩展,我可以从调用中放置“employee.employee”调用(作为 lambda 或其他东西)。
    【解决方案2】:

    我假设您的主要课程应该是 Employer 而不是 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);
            }
        }
    }
    

    【讨论】:

      【解决方案3】:

      我不确定你的参数应该表示什么,但如果第二个参数是谓词,你可能想做这样的事情:

      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);
         }
      }
      

      我可能还认为List&lt;T&gt; 上已经有这样的功能,所以如果 ToList 不是问题,你可以这样做

      employeList.Where(e => e.Employees.Count > 0).ToList().ForEach(Save);
      

      【讨论】:

        【解决方案4】:

        你可以用一个简单的扩展方法来做到这一点:

        employeeList.ForEach(e => Save(e));
        
        public static partial class IEnumerableExtensions
        {
            /// <summary>
            /// Executes an <see cref="Action&lt;T&gt;"/> 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&lt;T&gt;"/> in which each item should be processed.</param>
            /// <param name="action">The <see cref="Action&lt;T&gt;"/> 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);
            }
        }
        

        【讨论】:

          【解决方案5】:

          虽然传递一个 Action 可能很有用,但它不像迭代器那样灵活,它枚举树结构中的所有项目,使它们可用于其他 LINQ 运算符:

          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() );
          

          【讨论】:

            猜你喜欢
            • 2020-05-27
            • 2019-10-22
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2021-05-26
            • 2019-12-13
            • 1970-01-01
            • 2014-01-19
            相关资源
            最近更新 更多