博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
C#-LINQ
阅读量:5762 次
发布时间:2019-06-18

本文共 10019 字,大约阅读时间需要 33 分钟。

隐式类型

 C#3.0。.NET Fraemork3.5出来的。

编译器自动推算出来类型,语法糖。

#region 隐式类型var a = 1;var b = "1";var c = 1.23m;var d = 1.23;Console.WriteLine(a.GetType());Console.WriteLine(b.GetType());Console.WriteLine(c.GetType());Console.WriteLine(d.GetType());#endregion
View Code

关键字var,编译的时候会根据右边的类型判断左边变量的具体类型。

注意:是编译为中间语言的时候确定的。不影响性能。

匿名类型

 

#region 匿名类型string Name = "藏锋";//字段名字,Name根据上面定义的Name起名的。ID是自己定义的名字。var user = new { Name, ID = 1 };Console.WriteLine(user.Name);Console.WriteLine(user.ID);#endregion
View Code

不用实现写一个类,也可以创建一个对象,并且访问他的成员。

注意:成员名字可以自己定义。  也可以把其他成员放进来(默认名字和其他成员一样,也可以自己在指定)

自动属性

 

#region 自动属性   属性不能定义在方法里面int _UserId;public int UserID{    get { return _UserId; }    set { _UserId = value; }}//自动属性,生成的中间语言和  先定义一个私有变量,在写他的访问器一样。public string UserNmae { get; set; }#endregion
View Code

自动属性在编译的时候生成他对应的私有字段。和get ,set方法。

初始化器

 

#region 初始化var stuInit = new Student() { Name = "藏锋", ID = 1 };IList
listInt = new List
() { 1, 2, 3, 4 };//无论是值类型还是引用类型使用的时候都要初始化 赋值。//值类型一般有默认值0//引用类型为NULLStudent stuNULL;int intNULL;string strNULL;string refStr = string.Empty;int refID =0;Console.WriteLine(refStr);Console.WriteLine(refID);//传出参数使用的时候不需要赋值,原因在方法里面会给他赋值。string outStr;int outID;stuInit.GetUserName("WYX", ref refStr, ref refID, out outStr, out outID);#endregion
View Code

就是在对象创建的时候初始化他的一些值。

委托

 

#region 委托Student stuForDele = new Student();StuDele stuDele = new StuDele(stuForDele.GetUserId);stuDele += stuForDele.GetUserId1;stuDele += stuForDele.GetUserId2;stuDele += stuForDele.GetUserId3;stuDele -= stuForDele.GetUserId3;Console.WriteLine(stuDele(2));#endregion
View Code

委托是一个类型,和CLASS是一个级别的。

泛型

 泛型类,泛型方法,泛型委托

#region 泛型DAO
dao = new DAO
();Teacher teac = new Teacher();var pros1 = dao.GetAllPro();var pros2 = dao.GetAllPro(teac);Student stRef = new Student();var typeName = dao.GetTypeName
(stRef);foreach (var item in pros1){ Console.WriteLine(item);}foreach (var item in pros2){ Console.WriteLine(item);}Console.WriteLine(typeName);#endregion
泛型代码-代码过多只有一部分调用。源码页面下面下载

面试经常问的,泛型特点:1,类型安全。2,节约装箱拆箱操作,性能比较好。3,代码重用扩展性强。4,可读性强

1类型安全:用的是定义好的类型,不用object

2性能高:比如说原来一个方法要根据参数做不同的事,参数类型还不一样。那么就只能把参数设置为object,这样就要装箱拆箱操作了。

3代码重用:新建的类型只要符合泛型约束就可以用泛型的方法。

原理:泛型每次编译的时候都会都会生成一套对应的类方法。和在程序里面一个一个写方法一样的。

默认值:通过default关键字,将null赋予引用类型,将0赋予值类型。不能直接给泛型赋NULL或者0,因为不知道他是什么类型。

泛型委托

.NET框架自带三个系统泛型委托

1、public delegate bool Predicate<in T>(T obj);

//// 摘要://     表示定义一组条件并确定指定对象是否符合这些条件的方法。//// 参数://   obj://     要按照由此委托表示的方法中定义的条件进行比较的对象。//// 类型参数://   T://     要比较的对象的类型。此类型参数是逆变。即可以使用指定的类型或派生程度更低的类型。有关协变和逆变的更多信息,请参见泛型中的协变和逆变。//// 返回结果://     如果 obj 符合由此委托表示的方法中定义的条件,则为 true;否则为 false。
注释  in逆变(传入参数)  out协变(传出参数)

 一个传入参数,返回类型为bool

2、public delegate void Action<in T>(T obj);

//// 摘要://     封装一个方法,该方法只有一个参数并且不返回值。//// 参数://   obj://     此委托封装的方法的参数。//// 类型参数://   T://     此委托封装的方法的参数类型。此类型参数是逆变。即可以使用指定的类型或派生程度更低的类型。有关协变和逆变的更多信息,请参见泛型中的协变和逆变。
注释

没有返回值,0-16个传入参数

3、public delegate TResult Func<out TResult>();

//// 摘要://     封装一个不具有参数但却返回 TResult 参数指定的类型值的方法。//// 类型参数://   TResult://     此委托封装的方法的返回值类型。此类型参数是协变。即可以使用指定的类型或派生程度更高的类型。有关协变和逆变的更多信息,请参见泛型中的协变和逆变。//// 返回结果://     此委托封装的方法的返回值。
注释

最后一个参数为传出参数,一定有传出参数。前面0-16个传入参数。

#region 泛型委托Student stupre = new Student() { ID = 10, Name = "WYX" };Predicate
pStu = new Predicate
(stupre.CheUsreID);Console.WriteLine(pStu(stupre.ID));//0-16个参数 17中方法重载Action ac1 = new Action(stupre.GetUserNameNoP);ac1();Action
ac2 = new Action
(stupre.GetUserNameNoP);ac2("123");//最后一个参数为返回参数out 协变 ,前面可以放0-16个参数Func
fun1 = new Func
(stupre.ToString);fun1();Func
fun2 = new Func
(stupre.GetUserId3);Console.WriteLine(fun2(5));#endregion
三种框架泛型委托代码
//自定义泛型委托NoInOutDele
custom1 = new NoInOutDele
(stupre.GetUserNameP);Console.WriteLine(custom1());OnePDele
custom2 = new OnePDele
(stupre.GetUserNameP);Console.WriteLine(custom2("藏锋"));OutPDele
custom3 = new OutPDele
(stupre.GetUserNameNoP);Console.WriteLine(custom3("123", "456"));
自定义泛型委托

 

匿名方法

   delegate(){}

#region 匿名方法//委托里面添加的是一个个方法,可以写一个匿名方法给他   delegate(){} //好处 可以访问上下文变量OutPDele
unNameFun1 = new OutPDele
(delegate (string s1, string s2){ return s1 + s2;});Console.WriteLine(unNameFun1("藏", "锋"));#endregion
View Code

语法糖,编译器会帮我们声明一个方法。(CLS中规定的是只有方法和字段)

好处:可以访问上下文变量。

  什么代码可读性好,我感觉根据个人情况吧。编译到中间语言都一个样。

注意:匿名方法不能复制给变量      var g = delegate (string s) { Console.WriteLine(s); }

Lambda表达式

匿名方法的更加简单写法。把匿名方法的delegate换成=>。编译器也会把他生成一个方法。

无论是匿名方法还是Lambda表达式都是依赖于委托存在的。只有委托才调用他们。

定义:表达者委托或表达式树的匿名方法。  本质是一个匿名方法,有时候这种代码看不懂得时候就想想他的本质,委托调用他们。

#region LambdaOutPDele
lamdFun = new OutPDele
((string s1, string s2) =>{ return s1 + s2;});//=>读 gos to//一个参数可以(string s)=>s或者s=>s//多个参数(string s1,string s2.....) 一定加括号//返回只有一条语句返回 s=>{return s;} 或者s=>s (默认为返回)//多条语句 s=>{吧啦吧啦}OnePDele
lamdFun1 = new OnePDele
(s => { return s; });#endregion
View Code
//表达是一个委托OutPDele
lamdFunD = (string s1, string s2) => s1 + s2;lamdFunD.Invoke("1", "2");
View Code

 

Lambda表达式是一个匿名方法。语法:形参列表=>(goes to)方法体。“=>”运算符具有与“=”相同的优先级,并且是右结合性运算符。 

一个参数可以(string s)=>s或者s=>s
多个参数(string s1,string s2.....) 一定加括号
返回只有一条语句返回 s=>{return s;} 或者s=>s (默认为返回)。去掉了大括号

只有一条逻辑语句的例如   s=>{Console.WriteLine("1");}  或者s=>Console.WriteLine("1");  去掉了大括号

多条语句 s=>{吧啦吧啦}

 

Lambda表达式规则有三个

1、 Lambda包含的参数数量必须与委托类型包含的参数数量相同。

2、每个输入参数必须都能够隐式转换为其对应的委托参数。(逆变)

3、返回值(如果有)必须能够够转换为委托的返回类型。(协变)

扩展方法

.net framork3.0

目的:给一个类型增加行为

结构:静态类(不能嵌套,泛型),静态方法,方法第一个参数为 (this  扩展的类型   调用这个扩展方法的对象 ),后面参数为扩展方法的参数。(this string var, string s1)                     (this string var, string s1, string s2)

使用:在使用的类中添加他的命名空间,优先调用实例方法。  

#region 扩展方法string s = "藏锋";Console.WriteLine(s.MargTwoStr("1", "2"));Console.WriteLine(s.MarMySelf("23"));#endregion
View Code

注意:

Console.WriteLine(s.MarMySelf("23"));

//上面和下面编译玩之后都一样的。只是上面的写的更友好。
Console.WriteLine(StrExc.MarMySelf("1", "2"));

 THIS:四种用法

1,当前类的实例

public string GetUserNameNoP(string p_Name, string p_Name1){    return p_Name1 + this.Name + p_Name;}
View Code

2,串联构造函数

调用一个构造函数的时候,他再去调用另外 一个构造函数this(若干构造函数参数)

如果要构造基类的,就是base(若干构造函数参数)

public Teacher(){}public Teacher(string p_TeacherName):this(){}public Teacher(string p_TeacherName, DateTime p_CreateDate) : base(p_CreateDate){}
View Code

3,当前模块提到的扩展方法。

4,索引器

public class MyIndex{    private string[] strList = new string[10];    public string this[int index]    {        get { return strList[index]; }        set { strList[index] = value; }    }}public class MyIndexStr{    //用string索引查看的药用hashtable  key value列表    private Hashtable hash = new Hashtable();    public string this[string indexstr]    {        set { hash.Add(indexstr, value); }        get { return hash[indexstr].ToString(); }    }}
View Code

讲一个面试经历:有一个公司在咖啡馆面试的(其实是一个小公司,没有办公地点),所有开发语言都招,去了先根据业务设计一个算法,在填写的英文简历,通过后才面谈,刚开始讲一大推讽刺的话,在讲来我们这里可以学到好多东西,(深深的套路呀)。然后开始问专业问题,中括号怎么用的,我说  索引器,他愣了一下(后来我猜他问的是JQ里面[]属性选择器,他做前端的,可能不懂后台),后面就开始嘲讽我,然后我怼了他两句走了。见到这种人不要怂,他们也是能忽悠到一个算一个。

  还有一次,他们公司开发三个人,每天加班到八点,周六也加班,然后说实行末位淘汰制,我想三个人你还淘汰啥。

迭代器

foreach的时候就是进行迭代操作,但是foreach遍历的集合必须继承IEnumerable接口实现了GetEnumerator方法(IEnumerable只有GetEnumerator()一个方法)。

foreach数组的时候,其实编译器把foreach变成for了。

迭代器的优点:对于一个较大的集合,不用一次性把数据全部加在出来。不需要遍历的时候一次加在一个。

yield:在迭代器块中用于向枚举数对象提供值或发出迭代结束信号。(迭代器中用的就是这个东西,一次返回一个值

#region 迭代器   都要继承IEnumerable接口实现了GetEnumerator方法。List
teas = new List
();for (int i = 0; i < 10; i++){ Teacher tae = new Teacher() { CreateUserID = i, TeacherName = i.ToString(), CreateDate = DateTime.Now }; teas.Add(tae);}//我们foreach的时候就是使用的迭代器//但是数组也可以使用foreach,那是因为编译器把他转化为了for。中间语言中还是for//IEnumerable接口中只有一个方法。foreach (var item in teas){ Console.WriteLine(item.TeacherName);}foreach (var item in GetIterator()){ Console.WriteLine(item.ToString());}//foreach遍历的集合每次只会返回一个,不把所有的都加载。GetIterator()方法一段一段的执行,遍历中间结束,后面的代码也不会执行foreach (var item in GetIterator()){ if(item==2) { break; }}#endregion
View Code
//yield :在迭代器块中用于向枚举数对象提供值或发出迭代结束信号。static IEnumerable
GetIterator(){ Console.WriteLine("迭代器返回了1"); yield return 1; Console.WriteLine("迭代器返回了2"); //终止迭代 //yield break; yield return 2; Console.WriteLine("迭代器返回了3"); yield return 3;}
View Code

注意事项:

1:做foreach循环时多考虑线程安全性,在foreach时不要试图对被遍历的集合进行remove和add等操作任何集合,即使被标记为线程安全的,在foreach的时候,增加项和移除项的操作都会导致异常

2:IEnumerable接口是LINQ特性的核心接口,只有实现了IEnumerable接口的集合,才能执行相关的LINQ操作,比如select,where等这些操作。

LINQ

前面都是准备知识,现在进入正题。

分为两种模式

1,查询操作符(扩展方法+lambda),扩展方法  扩展的事IEnumerable<T>接口。所以linq的基础都是集合要继承IEnumerable<T>。

过滤:where,Find,FindAll,FindLast,First<T>,FirstOrDefault<>

统计函数:Count,MIn,Sum,Max

排序:OrderBy,OrderByDescending

跳过前面多少条数据取余下的数据:SKIP

从开始起获取指定数量的数据:TAKE

模糊匹配:Contains

分组:GroupBy        

连接查询:Join

投影:select(select t或者select(t=>new{t.1,t.2}))

2,查询表达式

From [type] id in source

[join [type] id in source  on expr equals expr [into subGroup]]

[from [type] id in source|let id=expr | where condition]

[orderby ordering,ordering,ordering,ordering...]

select select expr | group expr by key

[into id query]

 tips:type是可选的,id是数据源集合中的一项,source是数据源集合,   其实是在一直循环source,每次循环把值放入select后面

 

expr表示一个表达式,subGroup是一个临时变量,继承自IGroup,代表一个分组。

可以有多个form,多个where,set指定临时变量

可以有0-多个排序 orderby a descing orderby b。 orderby a descing ,b,c

select  new投影(匿名类,返回的类型用var   因为他生成的时候一后台定义的类型集合),group 类型 by 具体属性。标准linq  前面一般写一个var(不确定类型)。              一个查询表达式必须以select或者group by结束。select后跟要检索的内容。group by 是对检索的内容进行分组

group by 他后面只能跟 into is as 关键字,常用into 关键字放入临时数据源 然后在select 投影。

into放入临时数据源

Let 设置变量

注意:这种查询表达式会编译成上面的扩展方法+lambad。这种是一个语法糖。

 

推荐文章:

linqpad工具:   (EF也可以用)(但是linq to sql,EF中都不建议使用linq,特别是复杂的查询。linqtosql貌似已经死了)

转载于:https://www.cnblogs.com/wudequn/p/6726435.html

你可能感兴趣的文章