【问题标题】:Generic method to compare/filter two lists using expressions/lambda使用表达式/lambda 比较/过滤两个列表的通用方法
【发布时间】:2012-02-10 02:22:26
【问题描述】:

我想根据过滤器表达式比较两个列表;不确定如何为泛型方法构造 lambda 表达式;请参考下面的代码;或者有没有更简单的方法通过 LINQ 中的相交?

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            Data d1 = new Data {Id = 1, Name = "One"};
            Data d2 = new Data { Id = 2, Name = "Two" };
            Data d3 = new Data { Id = 3, Name = "Three" };

            Data d4 = new Data { Id = 1, Name = "One" };
            Data d5 = new Data { Id = 2, Name = "Two" };
            Data d6 = new Data { Id = 4, Name = "Four" };

            List<Data> original = new List<Data> {d1, d2, d3};
            List<Data> filterItems = new List<Data> {d4, d5, d6};

            List<Data> result = original.FilterDataList(filterItems);

            //How to call this method?
            List<Data> genericCall = original.FilterList<Data>(filterItems, data => data.Id ?????????????)
        }
    }

    public class Data
    {
        public long Id;
        public string Name;
    }

    public static class Extensions
    {
        public static List<Data> FilterDataList(this List<Data> sourceList, List<Data> filterOutItems)
        {
            return sourceList.Where(p => filterOutItems.All(l => l.Id != p.Id)).ToList();
        }

        public static List<T> FilterList<T>(this List<T> sourceList, List<T> filterOutItems, Func<T, bool> filterExpression)
        {
            return sourceList.Where(p => filterOutItems.All(filterExpression)).ToList();
        }
    }
}

【问题讨论】:

    标签: c# .net linq generic-list


    【解决方案1】:

    如果我正确理解您的问题,FilterListFilterDataList 的通用版本,您在其中将 lambda 作为参数传递。在这种情况下,您可以按如下方式调用该方法:

    List<Data> genericCall = original.FilterList<Data>(filterItems, (x, y) => x.Id != y.Id);
    

    如果你想像@ivancho 和@perelman 建议的那样使用 except,你可以使用这样的方法:

    public static class EnumerableExtension
    {
        public static IEnumerable<T> Except<T>(this IEnumerable<T> listA, IEnumerable<T> listB,
                                               Func<T, T, bool> lambda)
        {
            return listA.Except(listB, new Comparer<T>(lambda));
        }
    
        public static IEnumerable<T> Intersect<T>(this IEnumerable<T> listA, IEnumerable<T> listB,
                                                  Func<T, T, bool> lambda)
        {
            return listA.Intersect(listB, new Comparer<T>(lambda));
        }
    }
    

    然后你可以这样称呼它:

    original.Except<Data>(filterItems, (x, y) => x.Id != y.Id);
    

    【讨论】:

    • 啊哈!!除了扩展正是我所需要的!!!谢谢你:-) 是的,FilterList 是我一直在努力的通用方法, (x, y) => x.Id != y.Id 不能与 Func 一起使用,所以这就是我被卡住的地方
    【解决方案2】:

    您想要的输出是什么?您是否尝试过https://www.google.com/search?q=linq+intersect 中的第一个结果?您似乎应该阅读 Enumerable 文档 - 您正在使用 .All 最有可能表示 .Any 的地方,而且总的来说,它会让您更好地了解 LINQ 的可能性。

    【讨论】:

      【解决方案3】:

      我不清楚你想做什么。您的FilterDataList 似乎与Except().ToList() 相同。 FilterList 中的 .Where 不使用 p (lambda 的参数),所以我不清楚你想用过滤器表达式做什么。也许您正在寻找将不同的IEqualityComparerExcept() 一起使用,您必须将其定义为单独的类。

      【讨论】:

        【解决方案4】:

        感谢大家指出 LINQ 除了扩展,这是我的最终解决方案

        namespace ConsoleApplication1
        {
            class Program
            {
                static void Main(string[] args)
                {
                    Data d1 = new Data {Id = 1, Name = "One"};
                    Data d2 = new Data { Id = 2, Name = "Two" };
                    Data d3 = new Data { Id = 3, Name = "Three" };
        
                    Data d4 = new Data { Id = 1, Name = "One" };
                    Data d5 = new Data { Id = 2, Name = "Two" };
        
        
                    List<Data> original = new List<Data> {d1, d2, d3};
                    List<Data> filterItems = new List<Data> {d4, d5, d6};
        
        
                    List<Data> datas = original.Except(filterItems, (x, y) => x.Id == y.Id).ToList();
                }
            }
        
            public class Data
            {
                public long Id;
                public string Name;
            }
        
            public static class EnumerableExtension
            {
                public static IEnumerable<T> Except<T>(this IEnumerable<T> listA, IEnumerable<T> listB,
                                                       Func<T, T, bool> lambda)
                {
                    return listA.Except(listB, new Comparer<T>(lambda));
                }
        
                public static IEnumerable<T> Intersect<T>(this IEnumerable<T> listA, IEnumerable<T> listB,
                                                          Func<T, T, bool> lambda)
                {
                    return listA.Intersect(listB, new Comparer<T>(lambda));
                }
            }
        
        
            public class Comparer<T> : IEqualityComparer<T>
            {
                private readonly Func<T, T, bool> _expression;
        
                public Comparer(Func<T, T, bool> lambda)
                {
                    _expression = lambda;
                }
        
                public bool Equals(T x, T y)
                {
                    return _expression(x, y);
                }
        
                public int GetHashCode(T obj)
                {
                    /*
                     If you just return 0 for the hash the Equals comparer will kick in. 
                     The underlying evaluation checks the hash and then short circuits the evaluation if it is false.
                     Otherwise, it checks the Equals. If you force the hash to be true (by assuming 0 for both objects), 
                     you will always fall through to the Equals check which is what we are always going for.
                    */
                    return 0;
                }
            }
        
        
        }
        

        【讨论】:

          猜你喜欢
          • 2012-05-22
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2021-01-04
          • 2018-06-07
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多