hi,你好!欢迎访问本站!登录
本站由网站地图腾讯云宝塔系统阿里云强势驱动
当前位置:首页 - 教程 - 杂谈 - 正文 君子好学,自强不息!

.NET进阶篇05-Linq、Lambda表达式

2019-11-18杂谈搜奇网47°c
A+ A-

学问须要不停积聚、总结和沉淀,思索和写作是生长的催化剂

内容目次

一、Lambda表达式1、匿名要领2、Lambda表达式二、Linq概述三、查询操纵符1、linq初见2、经常运用查询操纵符挑选排序分组衔接兼并分页聚合转换四、并行Linq五、表达式树1、熟悉表达式目次树2、拼装表达式树3、运用六、小结

一、Lambda表达式

1、匿名要领

运用delegate的时刻许多时刻没必要运用一个一般要领,由于这个要领只要delegate会用,而且只用一次,这时刻运用匿名要领最合适。
匿名要领就是没有名字的要领。示例中就是用一个匿名要领建立托付实例,我们无需在写一个签字要领给托付,使代码更简约和可读。匿名要领也是在挪用时实行,在myDele(1,"test")处挪用。(用反编译器看一下照样会生成一个签字要领的,只不过在编译器内部运用)。

delegate bool MyDelegate(int i, string s);
MyDelegate myDele = delegate (int i, string s)
{
    Console.WriteLine($"我是匿名要领,参数值{i}{s}");
    return true;
};
bool b = myDele(1"test");

2、Lambda表达式

函数式编程,在C#3.0最先,我们有了Lambda表达式替代匿名要领,它比匿名要领越发简朴。Lambda运算符“=>”(发音goesto)的左侧列出了须要的参数,右侧是应用该参数要领的完成代码。

Action<string> a1 = delegate (string s) { Console.WriteLine(s); };
a1("匿名要领");
Action<string> a2 =  (string s)=> { Console.WriteLine(s); };
a1("Lambda表达式");
Action<string> a3 = s => { Console.WriteLine(s); };
a3("Lambda表达式,有一个参数的可以简写不要小括号,参数范例会自动揣摸");
Action<string> a4 = s =>  Console.WriteLine(s);
a4("Lambda表达式,要领体只要一行,连花括号也可以省略");

另一点,经由过程Lambda表达式可以接见Lambda表达式块外部的变量。这是一个异常好的功用,但假如未准确运用,也会异常风险。

int sommVal = 5;
Func<intint> f = x => x + sommVal;

sommVal = 7;
Console.WriteLine(f(3));

假如外部修正了sommVol值就会影响Lambda表达式的输出,特别是在多线程中,可以没法肯定当前的sommVal值。
Lambad表达式内部是怎样运用外部的变量呢?起首编译器会建立一个匿名类,然后将运用到的外部变量当作匿名类的组织函数的参数,当挪用时刻,就建立匿名类的一个实例,并通报挪用该要领时外部变量的值。

二、Linq概述

Linq(language integrated query)言语集成查询集成了C#编程言语中的查询语法,使之可以运用雷同的语法接见差别的数据源
依据数据源的差别,Linq可分为linq to object,linq to sql,linq to xml,你也可以扩大linq to excel,to everything。为差别的数据源供应雷同的查询接口即可。

三、查询操纵符

1、linq初见

如今我们有以下实体的鸠合

public class Student
{
    public int Id { getset; }
    public int ClassId { getset; }
    public string Name { getset; }
    public int Age { getset; }
}
List<Student> studentLst = new List<Student>();

假定studentLst里已经有一些数据,然后须要查询出年岁小于25的门生。有许多要领,可以轮回列表挑出age<25的门生,可以运用List的FindAll,Where等要领,看起来像下面如许(注重只要在接见list中数据时,才会去实行过滤前提查询,耽误查询)

var list = studentLst.Where<Student>(s => s.Age < 25); 
foreach (var item in list)
{
    Console.WriteLine("Name={0}  Age={1}", item.Name, item.Age);
}

where扩大要领的内部逻辑或许像如许,foreach轮回挪用过滤的托付要领,yield关键字语法糖包装了一些庞杂行动,包含会初始化一个IEnumerable类,然后给增加内容。

public static IEnumerable<T> Where<T>(this IEnumerable<T> source, Func<T, bool> func)
{
    if (source == null)
    {
        throw new Exception("source is null");
    }
    if (func == null)
    {
        throw new Exception("func is null");
    }

    foreach (var item in source)
    {
        if (func.Invoke(item))
        {
            yield return item;
        }
    }
}

那末用Linq怎样查询呢?

var list = from s in studentList
           where s.Age < 25
           select s;

foreach (var item in list)
{
    Console.WriteLine("Name={0}  Age={1}", item.Name, item.Age);
}

From、where、select都是预定义的关键字,查询表达式必需以from开首,以select或group子句完毕,中心可以运用where、orderby、join等。from子句引入数据源studentList和局限变量s,s就像foreach轮回中的迭代变量。
一样的,在运转时期定义查询表达式时,查询不会马上运转,在迭代数据项时运转。

2、经常运用查询操纵符

挑选

最常见的查询操纵就是以布尔表达式的情势运用挑选器。经由过程where子句挑选表达式为true的效果

var list = from s in studentList
           where s.Age < 25 && s.ClassId==1
           select s;
排序

Orderby子句依据要排序范例的默许比较器,对返回序列中的元素举行排序

var list = from s in studentList
           where s.Age < 25
           orderby s.Name ascending
           select s;

和list的以下要领相似,就是把关键字剖析为要领,跟着查询愈来愈庞杂,linq这类相似sql语句就表现的越发简约直观

var list = studentList.Where(s => s.Age < 25).OrderByDescending(s => s.Name).Select(s => s);
分组

group 子句用于对依据您指定的键所取得的效果举行分组。示例中into关键字建立进一步查询的标识,用select子句建立了一个带key和maxAge属性的匿名范例,返回每一个班级中岁数小于25岁的最大岁数。

var list = from s in studentList
           where s.Age < 25
           group s by s.ClassId into sg
           select new
           {
               key = sg.Key,
               maxAge = sg.Max(t => t.Age)
           };
foreach (var item in list)
{
    Console.WriteLine($"key={item.key}  maxAge={item.maxAge}");
}
衔接

运用join子句可以依据特征的前提兼并两个数据源。比方经由过程衔接查询挑选雷同课程的门生

List<Class> classList = new List<Class>(){
        new Class()
        {
            Id=1,
            ClassName="高数"
        },
        new Class()
        {
            Id=2,
            ClassName="毛概"
        }
};
var list = from s in studentList
           join c in classList on s.ClassId equals c.Id
           select new
           {
               Name = s.Name,
               CalssName = c.ClassName
           };
foreach (var item in list)
{
    Console.WriteLine($"Name={item.Name},CalssName={item.CalssName}");
}
兼并

zip要领是.NET4新增的,许可用一个函数把两个序列兼并为一个。第一个鸠合中的第一项会与第二个鸠合中的第一项兼并,第一个鸠合中的第二项与第二个鸠合中的第二项兼并,以此类推。假如两个鸠合的项目差别,zip要领就在抵达较小鸠合的末端时住手

var list = from s in studentList
           where s.Age < 25
           select s;
var list2 = from s in studentList
            where s.Age < 25
            select s;
var lst = list.Zip(list2, (first, second) => first.Name + "," + second.Name);
分页

扩大要领Take()和Skip()等的分区操纵用于分页。运用时把扩大要领take、skip增加到查询的末了,skip要领会疏忽依据页面大小和现实页数计算出的项数,再运用take要领依据页面大小提取肯定数目的项

int pageSize = 5;
int pageIdx = 0;
var list = (from s in studentList
            where s.Age < 25
            select s).Skip(pageIdx * pageSize).Take(pageSize);
聚合

聚合操纵符Count(),Sum(),Min(),Average()等不返回一个序列,而返回一个值。

转换

Linq不只是检索数据。 它也是用于转换数据的壮大东西。 经由过程运用 LINQ 查询,可以运用源序列作为输入,并经由过程多种体式格局对其举行修正,以建立新的输出序列。 经由过程排序和分组,你可以修正序列自身,而无需修正这些元素自身。 但或许 LINQ 查询最壮大的功用是建立新范例
以下示例将内存中数据构造中的对象转换为 XML 元素。

var studentsToXML = new XElement("Root",
            from student in studentList
            select new 
            XElement("student",
                    new XElement("name", student.Name),
                    new XElement("age", student.Age)
    )
);
Console.WriteLine(studentsToXML);

四、并行Linq

.NET4在System.Linq称号空间中包含了一个新类ParallelEnumerable,可以剖析查询的事情使其散布在多个线程上。鸠合序列会分红多个部份,差别的线程处置惩罚,完成后兼并。这对大鸠合,又是多核CPU的可以进步效力

var list = (from s in studentList.AsParallel()
           where s.Age < 25
           select s.Age).Sum();
var list2 = (from s in Partitioner.Create(studentList,true).AsParallel().WithDegreeOfParallelism(8)
            where s.Age < 25
            select s.Age).Sum();

可以运用Partitioner类建立分区器,WithDegreeOfParallelism指定最大并行使命数
并行linq每每须要较多耗时运用,那应当也有作废长时间运转的使命需求。给查询增加一个WithCancellation要领,并通报一个CancellationToken令牌作为参数。该查询在零丁线程中运用,主线程中触发作废敕令。

var cts = new CancellationTokenSource();
new Thread(()=>
    {
        try
        {
            var sum= (from s in studentList.AsParallel().WithCancellation(cts.Token)
                      where s.Age < 25
                      select s.Age).Sum();
        }
        catch (OperationCanceledException ex)
        {
            Console.WriteLine(ex.Message);
        }
    }
).Start();
//外部行动触发作废
cts.Cancel();

五、表达式树

1、熟悉表达式目次树

出如今System.Linq.Expression中,就是为Linq to sql效劳的。表达式树以树形数据构造示意代码,个中每一个节点都是一种表达式。它可以将我们本来直接由代码编写的逻辑存储在一个树状的构造里,然后运转的时刻就去动态剖析这个树。lambda表达式声明表达式目次树可以像下面如许。

Func<intintint> func = (m, n) => m * n + 2;// new Func<int, int, int>((m, n) => m * n + 2);
Expression<Func<intintint>> exp = (m, n) => m * n + 2;//lambda表达式声明表达式目次树
//Expression<Func<int, int, int>> exp1 = (m, n) =>//只能一行 不能有大括号
//    {
//        return m * n + 2;
//    };
//Queryable    //a=>a.Id>3

//表达式目次树:语法树,或许说是一种数据构造;可以被我们剖析
int iResult1 = func.Invoke(1223);
int iResult2 = exp.Compile().Invoke(1223);

2、拼装表达式树

假如运用Expression类接口声明看起来会像下面如许,注重比较Lambda表达式声明和Expression类本身拼装声明的区分,末了都是须要Compile()编译后实行。

Expression<Func<intintint>> exp = (m, n) => m * n + 2;
ParameterExpression parameterExpression = Expression.Parameter(typeof(int), "m");
ParameterExpression parameterExpression2 = Expression.Parameter(typeof(int), "n");
var multiply = Expression.Multiply(parameterExpression, parameterExpression2);
var constant = Expression.Constant(2typeof(int));
var add = Expression.Add(multiply, constant);

Expression<Func<intintint>> expression =
    Expression.Lambda<Func<intintint>>(
        add,
        new ParameterExpression[]
        {
             parameterExpression,
             parameterExpression2
        });

int iResult1 = exp.Compile().Invoke(1112);
int iResult2 = expression.Compile().Invoke(1112);

表达式树是由表达式的主体body、表达式的参数parameters、表达式范例Type、返回范例NodeType构成。一个树可以有许多叶子节点,庞杂一点的例子像下面如许

//i*j+w*x
ParameterExpression a = Expression.Parameter(typeof(int), "i");   //建立一个表达式树中的参数,作为一个节点,这里是最基层的节点
ParameterExpression b = Expression.Parameter(typeof(int), "j");
BinaryExpression r1 = Expression.Multiply(a, b);    //这里i*j,生成表达式树中的一个节点,比上面节点高一级

ParameterExpression c = Expression.Parameter(typeof(int), "w");
ParameterExpression d = Expression.Parameter(typeof(int), "x");
BinaryExpression r2 = Expression.Multiply(c, d);

BinaryExpression result = Expression.Add(r1, r2);   //运算两个中级节点,发生终结点

Expression<Func<intintintintint>> lambda = Expression.Lambda<Func<intintintintint>>(result, a, b, c, d);

Console.WriteLine(lambda + "");   //输出‘(i,j,w,x)=>((i*j)+(w*x))’,z对应参数b,p对应参数a

Func<intintintintint> f = lambda.Compile();  //将表达式树形貌的lambda表达式,编译为可实行代码,并生成该lambda表达式的托付;

Console.WriteLine(f(1111) + "");  //输出效果2

上面例子构成的表达式树就像下面如许。

3、运用

最经常运用的处所照样查询数据时。以往我们做一个查询,依据用户输入,去数据库中查询婚配的信息,可以去想到去拼一条带where前提的sql语句,然后去实行这条sql即可。
假如没法肯定须要查询的字段,当每换一个查询前提或组合多个查询前提,我们可以用表达式目次树动态的拼装起来。

还可以用来替代反射,我们晓得反射有机能题目,硬编码是最快的,但不够天真。像泛型一样,表达式树可以动态生成硬编码,缓存后今后接见挪用就相当于硬编码机能。比方示例中,我们假如须要对一个范例对象转换成另一个对象。这里可以有许多要领,硬编码、反射、序列化等都可以完成,如今我们用表达式树试一下。

People people = new People()
{
    Id = 11,
    Name = "Wang",
    Age = 31
};
PeopleCopy peopleCopy = new PeopleCopy()
{
    Id = people.Id,
    Name = people.Name,
    Age = people.Age
};

硬编码像上面如许,我们用表达式目次树就是为了可以生成这类硬编码的托付

public class ExpressionMapper
{
    private static Dictionary<stringobject> _Dic = new Dictionary<stringobject>();

    /// <summary>
    /// 字典缓存表达式树
    /// </summary>
    /// <typeparam name="TIn"></typeparam>
    /// <typeparam name="TOut"></typeparam>
    /// <param name="tIn"></param>
    /// <returns></returns>
    public static TOut Trans<TIn, TOut>(TIn tIn)
    {
        string key = string.Format("funckey_{0}_{1}"typeof(TIn).FullName, typeof(TOut).FullName);
        if (!_Dic.ContainsKey(key))
        {
            ParameterExpression parameterExpression = Expression.Parameter(typeof(TIn), "p");
            List<MemberBinding> memberBindingList = new List<MemberBinding>();
            foreach (var item in typeof(TOut).GetProperties())
            
{
                MemberExpression property = Expression.Property(parameterExpression, typeof(TIn).GetProperty(item.Name));
                MemberBinding memberBinding = Expression.Bind(item, property);
                memberBindingList.Add(memberBinding);
            }
            foreach (var item in typeof(TOut).GetFields())
            
{
                MemberExpression property = Expression.Field(parameterExpression, typeof(TIn).GetField(item.Name));
                MemberBinding memberBinding = Expression.Bind(item, property);
                memberBindingList.Add(memberBinding);
            }
            MemberInitExpression memberInitExpression = Expression.MemberInit(Expression.New(typeof(TOut)), memberBindingList.ToArray());
            Expression<Func<TIn, TOut>> lambda = Expression.Lambda<Func<TIn, TOut>>(memberInitExpression, new ParameterExpression[]
            {
                parameterExpression
            });
            Func<TIn, TOut> func = lambda.Compile();//拼装是一次性的
            _Dic[key] = func;
        }
        return ((Func<TIn, TOut>)_Dic[key]).Invoke(tIn);
    }
}
var result = ExpressionMapper.Trans<People, PeopleCopy>(people);

表达式和表达式树什么关系呢?起首表达式是匿名要领生成托付实例,而表达式树是一种数据构造,自身不能实行的,须要编译成sql,然后诠释实行表达式树中每一个节点的表达式。

六、小结

本章熟悉了Lambda表达式,linq查询以及相干的经常运用操纵符,它们不仅用于挑选数据源,给数据源排序,还用于实行分区,分组,转换,衔接等操纵,运用并行linq可以进步大型数据集的查询效力。另一个重要观点就是表达式目次树。表达式目次树许可在运转时期构建对数据源的查询,存储在顺序集合,重要用在linq to sql中,背面进修EntityFramework框架时会用到大批的表达式目次树。

 

 

debug everything

愿一觉醒来,阳光恰好

而不是,一觉醒来,天都黑了

 

假如手机在手边,也可以关注下vx:xishaobb,互动或猎取更多音讯。固然这里也一向更新de,下期见,拜了个拜拜。

  选择打赏方式
微信赞助

打赏

QQ钱包

打赏

支付宝赞助

打赏

  移步手机端
.NET进阶篇05-Linq、Lambda表达式

1、打开你手机的二维码扫描APP
2、扫描左则的二维码
3、点击扫描获得的网址
4、可以在手机端阅读此文章
未定义标签

本文来源:搜奇网

本文地址:https://www.sou7.cn/282178.html

关注我们:微信搜索“搜奇网”添加我为好友

版权声明: 本文仅代表作者个人观点,与本站无关。其原创性以及文中陈述文字和内容未经本站证实,对本文以及其中全部或者部分内容、文字的真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。请记住本站网址https://www.sou7.cn/搜奇网。

发表评论

选填

必填

必填

选填

请拖动滑块解锁
>>