• LINQ之延迟执行标准查询操作符(下)


    操作符:Join

    描述:基于匹配键对2个序列的元素进行关联,Join保留了TOuter中元素的顺序,并对于这些元素中的每一个元素,保留TInner中匹配元素的顺序.(来自msdn)

    原型:2种

    原型一:

    public static IEnumerable<TResult> Join<TOuter, TInner, TKey, TResult>(
    	this IEnumerable<TOuter> outer,
    	IEnumerable<TInner> inner,
    	Func<TOuter, TKey> outerKeySelector,
    	Func<TInner, TKey> innerKeySelector,
    	Func<TOuter, TInner, TResult> resultSelector
    )

    Join的参数比较多,需要特别注意。举个例子:

                List<Category> categories = new List<Category>
                                                {
                                                    new Category{Id = 1,Name = "Food"},
                                                    new Category{Id = 2,Name = "Toys"},
                                                    new Category{Id = 3,Name = "Fruit"}
                                                };
                List<Product> products = new List<Product>
                                             {
                                                 new Product{ProductId = 201,ProductName = "Chuckit",CategoryId = 2},
                                                 new Product{ProductId = 202,ProductName = "SafeMade",CategoryId = 2},
                                                 new Product{ProductId = 101,ProductName = "Taste",CategoryId = 1},
                                                 new Product{ProductId = 102,ProductName = "Canidae",CategoryId = 1},
                                                 new Product{ProductId = 103,ProductName = "Flavor",CategoryId = 1}
    
                                             };
    
                var productList = categories.Join(products, c => c.Id, p => p.CategoryId,
                                                  (c, p) => new { c.Name, p.ProductName });
    
                foreach (var product in productList)
                {
                    Console.WriteLine("{0}----{1}", product.Name, product.ProductName);
                }

    在这个例子中,我们将categories和products通过categories.Id和products.CategoryId进行关联,返回一个新的对象的序列。

    原型二略。


    操作符:GroupJoin

    描述:基于键相等对两个序列的元素进行关联并对结果进行分组.(来自msdn)

    原型:

    public static IEnumerable<TResult> GroupJoin<TOuter, TInner, TKey, TResult>(
    	this IEnumerable<TOuter> outer,
    	IEnumerable<TInner> inner,
    	Func<TOuter, TKey> outerKeySelector,
    	Func<TInner, TKey> innerKeySelector,
    	Func<TOuter, IEnumerable<TInner>, TResult> resultSelector
    )

    GroupJoin和Join很类似,GroupJoin会对返回的结果给予TOuter的Key进行分组。

    对于返回的结果,TOuter中的一个元素可能对应多个TInner元素。

    注意:如果TInner中没有TOuter的给定元素的关联元素,则该元素的匹配序列为空,但仍将出现在结果中.

    举个例子:

    List<Category> categories = new List<Category>
                                                {
                                                    new Category{Id = 1,Name = "Food"},
                                                    new Category{Id = 2,Name = "Toys"},
                                                    new Category{Id = 3,Name = "Fruit"}
                                                };
                List<Product> products = new List<Product>
                                             {
                                                 new Product{ProductId = 201,ProductName = "Chuckit",CategoryId = 2},
                                                 new Product{ProductId = 202,ProductName = "SafeMade",CategoryId = 2},
                                                 new Product{ProductId = 101,ProductName = "Taste",CategoryId = 1},
                                                 new Product{ProductId = 102,ProductName = "Canidae",CategoryId = 1},
                                                 new Product{ProductId = 103,ProductName = "Flavor",CategoryId = 1}
    
                                             };
    
                var productList = categories.GroupJoin(products, c => c.Id, p => p.CategoryId,
                                                  (c, ps) => new { c.Name,Products= ps.Select(p=>p.ProductName) });
    
                foreach (var product in productList)
                {
                    Console.WriteLine(product.Name+":");
                    foreach (var item in product.Products)
                    {
                        Console.WriteLine(item);
                    }
                }

    打印出来的结果是:

    Food:
    Taste
    Canidae
    Flavor
    Toys:
    Chuckit
    SafeMade
    Fruit:

    我们可以发现Fruit这个category没有匹配的产品,但是也被打印出来了。


    操作符:GroupBy

    描述:GroupBy主要用来对输入序列进行分组,并且返回IGrouping<K, T>

    原型:GroupBy提供了众多的原型,我们主要关注4个:

    原型一:

    public static IEnumerable<IGrouping<TKey, TSource>> GroupBy<TSource, TKey>(
    	this IEnumerable<TSource> source,
    	Func<TSource, TKey> keySelector
    )
                List<Product> products = new List<Product>
                                             {
                                                 new Product{ProductId = 201,ProductName = "Chuckit",CategoryId = 2},
                                                 new Product{ProductId = 202,ProductName = "SafeMade",CategoryId = 2},
                                                 new Product{ProductId = 101,ProductName = "Taste",CategoryId = 1},
                                                 new Product{ProductId = 102,ProductName = "Canidae",CategoryId = 1},
                                                 new Product{ProductId = 103,ProductName = "Flavor",CategoryId = 1}
    
                                             };
    
                var productList = products.GroupBy(p => p.CategoryId).OrderBy(item=>item.Key);
    
                foreach (var keyGroupProducts in productList)
                {
                    Console.WriteLine(keyGroupProducts.Key + ":");
                    foreach (var product in keyGroupProducts)
                    {
                        Console.WriteLine(product.ProductName);
                    }
                }

    输出结果:

    1:
    Taste
    Canidae
    Flavor
    2:
    Chuckit
    SafeMade

    原型一比较简单,主要包含2个参数,一个输入序列,另外一个是selector。

    在这个例子中,我们通过对products序列进行按categoryid进行分组,然后按照分组后的key进行排序。

    原型二:

    public static IEnumerable<IGrouping<TKey, TSource>> GroupBy<TSource, TKey>(
    	this IEnumerable<TSource> source,
    	Func<TSource, TKey> keySelector,
    	IEqualityComparer<TKey> comparer
    )

    原型二为我们多提供了IEqualityComparer参数用以自定义比较方法

    public interface IEqualityComparer<in T>

    这个接口提供了2个方法bool Equals(T x, T y)和int GetHashCode(T obj)2个方法

     

    注意IEqualityComparer是根据Key来比较的,例如,我们可以写个comparer比较器:

    public class ProductEqualityComparer : IEqualityComparer<int>
        {
            public bool Equals(int x, int y)
            {
                if (x == 0) return false;
                if (y == 0) return false;
                return IsSameVendor(x) == IsSameVendor(y);
            }
    
            public int GetHashCode(int obj)
            {
                int start = 1;
                int end = 100;
                return IsSameVendor(obj) ? start.GetHashCode() : end.GetHashCode();
            }
    
            public bool IsSameVendor(int id)
            {
                return id < 100;
            }
        }
    ProductEqualityComparer comparer = new ProductEqualityComparer();
                List<Product> products = new List<Product>
                                             {
                                                 new Product{ProductId = 201,ProductName = "Chuckit",CategoryId = 2},
                                                 new Product{ProductId = 202,ProductName = "SafeMade",CategoryId = 200},
                                                 new Product{ProductId = 101,ProductName = "Taste",CategoryId = 1},
                                                 new Product{ProductId = 102,ProductName = "Canidae",CategoryId = 1},
                                                 new Product{ProductId = 103,ProductName = "Flavor",CategoryId = 1}
    
                                             };
    
                IEnumerable<IGrouping<int, Product>> productList = products.GroupBy(p => p.CategoryId, comparer);
    
                foreach (var keyGroupProducts in productList)
                {
                    Console.WriteLine(keyGroupProducts.Key + ":"+(comparer.IsSameVendor(keyGroupProducts.Key)?"Same Vendor":"Diff Vendor"));
                    foreach (var product in keyGroupProducts)
                    {
                        Console.WriteLine(product.ProductName);
                    }
                }
     
    原型三:
     
    public static IEnumerable<IGrouping<TKey, TElement>> GroupBy<TSource, TKey, TElement>(
    	this IEnumerable<TSource> source,
    	Func<TSource, TKey> keySelector,
    	Func<TSource, TElement> elementSelector
    )

    与原型一不一样的是,原型一返回了与输入序列同样的类型元素,原型三可以根据自己的需求,返回所需的元素类型,我们来看个例子:

     

    List<Product> products = new List<Product>
                                             {
                                                 new Product{ProductId = 201,ProductName = "Chuckit",CategoryId = 2},
                                                 new Product{ProductId = 202,ProductName = "SafeMade",CategoryId = 2},
                                                 new Product{ProductId = 101,ProductName = "Taste",CategoryId = 1},
                                                 new Product{ProductId = 102,ProductName = "Canidae",CategoryId = 1},
                                                 new Product{ProductId = 103,ProductName = "Flavor",CategoryId = 1}
    
                                             };
    
                var productList = products.GroupBy(p => p.CategoryId, p=>new{p.ProductId,p.ProductName}).OrderBy(p=>p.Key);
    
                foreach (var keyGroupProducts in productList)
                {
                    Console.WriteLine("Category: "+keyGroupProducts.Key);
                    foreach (var product in keyGroupProducts)
                    {
                        Console.WriteLine("{0}-{1}",product.ProductId, product.ProductName);
                    }
                }

     

    我们返回的不再是Product,而是我们新建的一个匿名类型,包含ProductId和ProductName

     

    原型四:

     

    public static IEnumerable<IGrouping<TKey, TElement>> GroupBy<TSource, TKey, TElement>(
    	this IEnumerable<TSource> source,
    	Func<TSource, TKey> keySelector,
    	Func<TSource, TElement> elementSelector,
    	IEqualityComparer<TKey> comparer
    )

    原型四实际上是原型二和原型三的结合,比较简单,就不举例子了。

     


    操作符:Distinct

    描述:Distinct操作符主要用于过滤重复的元素,2个元素相等是通过GetHashCode和Equals来判断的,原型二为我们提供了自己写对等条件的方法

    原型:2种

    原型一:

    public static IEnumerable<TSource> Distinct<TSource>(
    	this IEnumerable<TSource> source
    )

    这个原型很简单,我们借用msdn的例子来说明:

    List<int> ages = new List<int> { 21, 46, 46, 55, 17, 21, 55, 55 };
    
                IEnumerable<int> distinctAges = ages.Distinct();
    
                Console.WriteLine("Distinct ages:");
    
                foreach (int age in distinctAges)
                {
                    Console.WriteLine(age);
                }

    输出结果:

    Distinct ages:
    21
    46
    55
    17

    重复的46,55被过滤掉了。

    原型二:

    public static IEnumerable<TSource> Distinct<TSource>(
    	this IEnumerable<TSource> source,
    	IEqualityComparer<TSource> comparer
    )

    我们先定义一个相等比较器:

    public class ProductEqualityComparer : IEqualityComparer<Product>
        {
            public bool Equals(Product x, Product y)
            {
                if (x == null||y==null) return false;
                return x.ProductId == y.ProductId && x.CategoryId == y.CategoryId;
            }
    
            public int GetHashCode(Product obj)
            {
                int productid = obj.ProductId;
                int categoryid = obj.CategoryId;
                return productid.GetHashCode() + categoryid.GetHashCode();
            }
        }

    然后,在查询的时候,应用这个比较器:

    ProductEqualityComparer comparer = new ProductEqualityComparer();
                List<Product> products = new List<Product>
                                             {
                                                 new Product{ProductId = 201,ProductName = "Chuckit",CategoryId = 2},
                                                 new Product{ProductId = 201,ProductName = "CHUCKIT",CategoryId = 2},
                                                 new Product{ProductId = 101,ProductName = "Taste",CategoryId = 1},
                                                 new Product{ProductId = 102,ProductName = "Canidae",CategoryId = 1},
                                                 new Product{ProductId = 103,ProductName = "Flavor",CategoryId = 1}
    
                                             };
    
                var productList = products.Distinct(comparer);
    
                foreach (var product in productList)
                {
                    Console.WriteLine("{0}:{1}:{2}",product.CategoryId,product.ProductId,product.ProductName);
                }

    输出结果为:

    2:201:Chuckit
    1:101:Taste
    1:102:Canidae
    1:103:Flavor

    可以发现,201这个product第二个ProductName为全大写的被过滤掉了,因为根据我们的相等比较器,两个201product是一样的。


    操作符:Union

    描述:连接2个序列,相同的元素只会保留一个

    原型:2种

    原型一:

    public static IEnumerable<TSource> Union<TSource>(
    	this IEnumerable<TSource> first,
    	IEnumerable<TSource> second
    )

    这也是个很简单的操作符,我们还是来看个简单的例子:

    List<int> int1 = new List<int>{12,42,3,2};
                List<int> int2 = new List<int> {24, 3 };
    
                var unitInts = int1.Union(int2);
                foreach (var unitInt in unitInts)
                {
                    Console.WriteLine(unitInt);
                }

    输出结果为:

    12
    42
    3
    2
    24

    int2的3被过滤掉了,因为int1已经存在3这个元素了

    原型二为我们提供了相等比较器:

    public static IEnumerable<TSource> Union<TSource>(
    	this IEnumerable<TSource> first,
    	IEnumerable<TSource> second,
    	IEqualityComparer<TSource> comparer
    )

    跟上面的Distinct的原型二类似,我们就不再举例了。


    操作符:Intersect

    描述:返回2个序列的交集

    原型:2种

    题外话:看到这里,其实我们就很容易猜出这2种原型是什么了,无非一种是采用默认对等比较的交集操作,另一种是可以自己提供相等比较器的方法

    原型一:

    public static IEnumerable<TSource> Intersect<TSource>(
    	this IEnumerable<TSource> first,
    	IEnumerable<TSource> second
    )

    原型二:

    public static IEnumerable<TSource> Intersect<TSource>(
    	this IEnumerable<TSource> first,
    	IEnumerable<TSource> second,
    	IEqualityComparer<TSource> comparer
    )

    我们举个原型一的例子吧:

    List<int> int1 = new List<int> { 12, 42, 3, 2 };
                List<int> int2 = new List<int> { 24, 3 };
    
                var unitInts = int1.Intersect(int2);
                foreach (var unitInt in unitInts)
                {
                    Console.WriteLine(unitInt);
                }

    结果集只会返回3。


    操作符:Except

    描述:该操作符返回存在于序列1但是不存在序列2的所有元素

    原型:2种

    原型一:

    public static IEnumerable<TSource> Except<TSource>(
    	this IEnumerable<TSource> first,
    	IEnumerable<TSource> second
    )

    原型二:

    public static IEnumerable<TSource> Except<TSource>(
    	this IEnumerable<TSource> first,
    	IEnumerable<TSource> second,
    	IEqualityComparer<TSource> comparer
    )

    我们举个原型一的例子:

                List<int> int1 = new List<int> { 12, 42, 3, 2};
                List<int> int2 = new List<int> { 24, 3 };
    
                var unitInts = int1.Except(int2);
                foreach (var unitInt in unitInts)
                {
                    Console.WriteLine(unitInt);
                }

    返回的结果集为:

    12
    42
    2

    元素3在int2中,所以被过滤掉了,int2中的元素24,不在int1中,所以不会被返回回来。


    操作符:Cast

    描述:将非泛型的结果皆转成泛型结果集,以支持大部分的标准查询操作符。

    如果我们有一个序列是在.net 2.0框架上做的,但是,我们又希望用LINQ来操作,此时,我们就可以先将该序列Cast操作后转成IEnumerable<T>.

    原型:

    public static IEnumerable<TResult> Cast<TResult>(
    	this IEnumerable source
    )

    借用msdn的例子来说明:

    System.Collections.ArrayList fruits = new System.Collections.ArrayList();
                fruits.Add("apple");
                fruits.Add("mango");
    
                IEnumerable<string> query =
                    fruits.Cast<string>().Select(fruit => fruit);
    
                foreach (string fruit in query)
                {
                    Console.WriteLine(fruit);
                }

    操作符:OfType

    描述:将可以转成某种类型的元素作为结果集输出来,跟Cast一样,OfType的输入序列也是非泛型的IEnumerable

    原型:

    public static IEnumerable<TResult> OfType<TResult>(
    	this IEnumerable source
    )

    还是借用msdn的例子:

    System.Collections.ArrayList fruits = new System.Collections.ArrayList(4);
                fruits.Add("Mango");
                fruits.Add("Orange");
                fruits.Add("Apple");
                fruits.Add(3.0);
                fruits.Add("Banana");
    
                // Apply OfType() to the ArrayList.
                IEnumerable<string> query1 = fruits.OfType<string>();
    
                Console.WriteLine("Elements of type 'string' are:");
                foreach (string fruit in query1)
                {
                    Console.WriteLine(fruit);
                }

    ArrayList的签名为:

    public class ArrayList : IList, ICollection, 
    	IEnumerable, ICloneable

    它是没有实现IEnumerable<T>,经过OfType后,返回的结果集就支持IEnumerable了。


    操作符:AsEnumerable

    描述:输入一个IEnumerable<T>序列,返回一个IEnumerable<T>序列。

    看到这个描述也许你会觉得很好玩:这不是多此一举吗,没有任何操作,只是为了返回跟输入序列一样的序列。

    在稍后的LINQ TO SQL我们再来详细讨论这个问题。

    原型:

    public static IEnumerable<TSource> AsEnumerable<TSource>(
    	this IEnumerable<TSource> source
    )


    操作符:DefaultIfEmpty

    原型一:

    public static IEnumerable<TSource> DefaultIfEmpty<TSource>(
    	this IEnumerable<TSource> source
    )

    原型二:

    public static IEnumerable<TSource> DefaultIfEmpty<TSource>(
    	this IEnumerable<TSource> source,
    	TSource defaultValue
    )

    描述:该操作符主要用来描述,如果序列中的元素为空,该返回什么样的元素。原型一采用默认的对象的构造函数来创建一个空的对象,原型二允许我们定义一个默认的元素。

    借用msdn的例子来说明原型二:

        class Pet
                {
                    public string Name { get; set; }
                    public int Age { get; set; }
                }
    
                public static void DefaultIfEmptyEx2()
                {
                    Pet defaultPet = new Pet { Name = "Default Pet", Age = 0 };
    
                    List<Pet> pets1 =
                        new List<Pet>{ new Pet { Name="Barley", Age=8 },
                                       new Pet { Name="Boots", Age=4 },
                                       new Pet { Name="Whiskers", Age=1 } };
    
                    foreach (Pet pet in pets1.DefaultIfEmpty(defaultPet))
                    {
                        Console.WriteLine("Name: {0}", pet.Name);
                    }
    
                    List<Pet> pets2 = new List<Pet>();
    
                    foreach (Pet pet in pets2.DefaultIfEmpty(defaultPet))
                    {
                        Console.WriteLine("\nName: {0}", pet.Name);
                    }
                }
    

    输出结果为:

    Name: Barley
    Name: Boots
    Name: Whiskers
    
    Name: Default Pet
    第二个序列为空,因此输出了我们事先定义的默认的对象.
     


    操作符:Range

    原型:

     

    public static IEnumerable<int> Range(
    	int start,
    	int count
    )

    该操作符返回输入序列的一部分元素

     


    操作符:Repeat

    原型:

     

    public static IEnumerable<TResult> Repeat<TResult>(
    	TResult element,
    	int count
    )

    对元素element循环count次数


    操作符:Empty

    原型:

    public static IEnumerable<TResult> Empty<TResult>()

    描述:这是一个静态方法,但不是一个扩展方法。

    IEnumerable<string> strings = Enumerable.Empty<string>();
    foreach(string s in strings)
    Console.WriteLine(s);
    Console.WriteLine(strings.Count());

    输出结果为:

    0

  • 相关阅读:
    路面修整
    路由器安置
    高维网络
    SRETAN
    对象、数组 深度复制,支持对象嵌套数组、数组嵌套对象
    仿 window对象 confirm方法
    仿 window对象 alert 方法
    饼状图
    柱状图
    树状图
  • 原文地址:https://www.cnblogs.com/tian2010/p/2400921.html
Copyright © 2020-2023  润新知