Where方法

 每一项数据都会经过predicate的测试,如果针对一个元素,predicate执行的返回值为true,那么这个元素就会放到返回值中。
Where参数是一个lambda表达式格式的匿名方法,方法的参数e表示当前判断的元素对象。参数的名字不一定非要叫e,不过一般lambda表达式中的变量名长度都不长。

 static void Main(string[] args)
 {
     List<Employee> list = new List<Employee>();
     list.Add(new Employee { Id = 1, Name = "张三", Age = 30, Gender = false, Salary = 3500});
     list.Add(new Employee { Id = 2, Name = "李四", Age = 48, Gender = false, Salary = 6000});
     list.Add(new Employee { Id = 3, Name = "王五", Age = 16, Gender = true, Salary = 2800});
     list.Add(new Employee { Id = 4, Name = "赵六", Age = 16, Gender = false, Salary = 4100});
     list.Add(new Employee { Id = 5, Name = "田七", Age = 33, Gender = true, Salary = 3000});

     IEnumerable<Employee> items = list.Where(e => e.Age > 40);
     foreach (var item in items)
     {
         Console.WriteLine(item);
     }
 }
 class Employee
 {
     public long Id { get; set; }
     public string Name { get; set; }//姓名
     public int Age { get; set; }//年龄
     public bool Gender { get; set; }//性别
     public int Salary { get; set; }//月薪
     public override string ToString()
     {
         return $"Id={Id},Name={Name},Age={Age},Gender={Gender},Salary={Salary}";
     }
 }

Count()方法

获取数据条数

int count1 = list.Count(e => e.Salary > 5000 || e.Age < 30);
int count2 = list.Where(e => e.Salary > 5000 || e.Age < 30).Count();

Any()方法

是否至少有一条数据

bool b1 = list.Any(e => e.Salary > 8000);
bool b2 = list.Where(e => e.Salary > 8000).Any();

比Count()实现效率高。

获取一条数据(是否带参数的两种写法)

Single

有且只有一条满足要求的数据,如果都不满足或者满足多条数据则抛出异常。

Employee e1= list.Where(e => e.Id == 4).Single();
Employee e2= list.Single(e => e.Id == 4);
Console.WriteLine(e2);

SingleOrDefault

最多只有一条满足要求的数据,如果都不满足返回默认值,如果满足多条则抛出异常。

Employee e2 = list.SingleOrDefault(e => e.Id == 4);
Employee e3 = list.SingleOrDefault(e => e.Id == 10);
Console.WriteLine(e3 == null);

First

至少有一条,返回第一条,如果都不满足抛出异常

Employee e4 = list.First(e => e.Age > 30);
Employee e5 = list.First(e => e.Age > 100);

FirstOrDefault

返回第一条或者默认值

Employee e6 = list.FirstOrDefault(e => e.Age > 100);
Console.WriteLine(e6 == null);

排序

Order() 对数据正序排序;

list.OrderBy(e => e.Age);

OrderByDescending() 倒序排序;

IEnumerable<Employee>  list2= list.OrderByDescending(e => e.Age);

用Guid或者随机数进行随机排序:

IEnumerable<Employee>  list2= list.OrderBy(e => Guid.NewGuid());

Random random = new Random(); 
IEnumerable<Employee>  list2= list.OrderBy(e => random.Next());

按照最后一个字符排序:

IEnumerable<Employee>  list2= list.OrderBy(e => e.Name[e.Name.Length - 1]);

多规则排序

可以在Order()OrderByDescending()后继续写ThenBy ()ThenByDescending()
优先按照Age排序,如果Age相同再按照Salary排序

list.OrderBy(e => e.Age).ThenByDescending(e => e.Salary)
// 千万不要写成
// list.OrderBy(e => e.Age).OrderByDescending(e => e.Salary)

限制结果集,获取部分数据

Skip(n)跳过n条数据,Take(n)获取n条数据。
获取从第2条开始获取3条数据:

var orderedItems1 = list.Skip(2).Take(3);

Skip()Take()也可以单独使用。

var orderedItems1 = list.Skip(2);
var orderedItems2 = list.Take(3);

Tips:LINQ中所有的扩展方法几乎都是针对IEnumerable接口的,而几乎所有能返回集合的都返回IEnumerable,所以是可以把几乎所有方法“ 链式使用 ”的。

聚合函数

Max()Min()Average()Sum()Count()

//最大年龄
int a = list.Max(e => e.Age);
//最小年龄
int a = list.Min(e => e.Age);

//年龄大于等于30岁的平均年龄
double b = list.Where(e => e.Age >= 30).Average(e => e.Age);

//集合中所有年龄的和
int sum = list.Sum(e => e.Age);
//集合个数
int count = list.Count();

分组

GroupBy()方法参数是分组条件表达式,返回值为IGrouping<TKey, TSource>类型的泛型IEnumerable,也就是每一组以一个IGrouping对象的形式返回。IGrouping是一个继承自IEnumerable的接口,IGroupingKey属性表示这一组的分组数据的值。

IEnumerable<IGrouping<int, Employee>> g = list.GroupBy(x => x.Age);
foreach (IGrouping<int, Employee> item in g)
{
    Console.WriteLine(item.Key);
    foreach (var e in item)
    {
        Console.WriteLine(e);
    }

} 

投影

 投影是把集合中的每一项转换为另外一种类型。

IEnumerable<int> ages = list.Select(e => e.Age);
IEnumerable<string> names = list.Select(e=>e.Gender?"男":"女");
var dogs = list.Select(p=>new Dog{NickName=e.Name,Age=e.Age});

匿名类型

var p = new {Name="tom", Id=1};
//属性名称一样时,可以省略
var p1 = new {name, Id=1, p.Age};
var select = list.Select(e => new { NianLing = e.Age, XingBie = e.Gender ? "男" : "女" });
foreach (var item in select)
{
  Console.WriteLine(item.NianLing + "," + item.XingBie);
}      
var items = list.GroupBy(e => e.Gender)
                .Select(g=>new { Gender=g.Key,Count=g.Count(),AvgSalary= g.Average(e => e.Salary),MinAge= g.Min(e => e.Age)});

通过反编译软件查看dll文件,编译器最后也是生成了具体的类,只不过这个类名是编译器自动生成的。

集合转换

 有一些地方需要数组类型或者List类型的变量,可以用ToArray()方法和ToList()分别把IEnumerable<T>转换为数组类型和List<T>类型。

查询语法

 使用Where、OrderBy、Select等 扩展方法进行数据查询的写法叫做 “LINQ方法语法”。还有一种“查询语法”的写法。

var items2 = from e in list
            where e.Salary > 3000
            orderby e.Age
            select new { e.Name, e.Age, Gender = e.Gender ? "男" : "女" };
//等同于以下写法
var items3 = list.Where(e => e.Salary > 3000).OrderBy(e => e.Age).
    Select(e => new { e.Name, e.Age, Gender = e.Gender ? "男" : "女" });

Tips:日常开发推荐方法语法

统计一个字符串中每个字母出现的频率(忽略大小写),然后按照从高到低的顺序输出出现频率高于2次的单词和其出现的频率:

var items = s.Where(c => char.IsLetter(c))//过滤非字母
    .Select(c => char.ToLower(c))//大写字母转换为小写
    .GroupBy(c => c)//根据字母进行分组
    .Where(g=>g.Count()>2)//过滤掉出现次数<=2
    .OrderByDescending(g => g.Count())//按次数排序
    .Select(g=>new { Char=g.Key,Count=g.Count()});

委托

1、委托是可以指向方法的类型,调用委托变量时执行的就是变量指向的方法。

static void Main(string[] args)
{
    D1 d = F1;
    d();
}
static void F1()
{
    Console.WriteLine("hello");
}
delegate void D1();

匿名方法

static void Main(string[] args)
{
    Action a = delegate() {
        Console.WriteLine("hello world");
    };
    a();
}
static void Main(string[] args)
{
    Action a = delegate() {
        Console.WriteLine("hello world");
    };
    Func<int, int, int> func = delegate (int a, int b)
    {
        return a + b;
    };
    Console.WriteLine(func(1, 2));
}

Lambda表达式

Func<int, int, string> f1 = (i1,i2) =>{
    return $"{i1}+{i2}={i1 + i2}";
};
  • 可以省略参数数据类型,因为编译能根据委托类型推断出参数的类型,用=>引出来方法体。
  • 如果委托没有返回值,且方法体只有一行代码,可省略 {}
  • 如果=>之后的方法体中只有一行代码,且方法有返回值,那么可以省略方法体的{}以及return。
  • 如果只有一个参数,参数的()可以省略。

2、.NET 中定义了泛型委托Action(无返回值)和Func(有返回值),所以一般不用自定义委托类型。

static void Main(string[] args)
{
    Action a = F1;
    a();
}
static void F1()
{
    Console.WriteLine("hello");
}
static void Main(string[] args)
{
    Func<int, int, int> func = F2;
    int sum = func(2, 5);
    Console.WriteLine(sum);
}
static int F2(int a, int b)
{
    return a + b;
}

LINQ

筛选出数组中大于3的数字:

IEnumerable<int> ints = [ 2,3,4,5,5];
IEnumerable<int> result = ints.Where(a => a > 3);

通过编写扩展方法MyWhere来模拟Where的实现:

static void Main(string[] args)
{
    IEnumerable<int> ints = [ 2,3,4,5,5];
    //IEnumerable<int> result = ints.Where(a => a > 3);
    IEnumerable<int> result = MyWhere(ints, a => a > 3);
    foreach (var item in result)
    {
        Console.WriteLine(item);
    }
}

static IEnumerable<int> MyWhere(IEnumerable<int> ints, Func<int, bool> func)
{
    List<int> result = new List<int>();
    foreach (var item in ints)
    {
        if(func(item)) result.Add(item);
    }
    return result;
}

yield return

通过yield return来让MyWhere“流水线”处理:

static void Main(string[] args)
{
    IEnumerable<int> ints = [ 2,3,4,5,5];
    //IEnumerable<int> result = ints.Where(a => a > 3);
    IEnumerable<int> result = MyWhere(ints, a => a > 3);
    foreach (var item in result)
    {
        Console.WriteLine(item);
    }
}
static IEnumerable<int> MyWhere(IEnumerable<int> ints, Func<int, bool> func)
{
    List<int> result = new List<int>();
    foreach (var item in ints)
    {
        if(func(item)) yield return item;
    }
}
03-22 07:17