隐式类型
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
关键字var,编译的时候会根据右边的类型判断左边变量的具体类型。
注意:是编译为中间语言的时候确定的。不影响性能。
匿名类型
#region 匿名类型string Name = "藏锋";//字段名字,Name根据上面定义的Name起名的。ID是自己定义的名字。var user = new { Name, ID = 1 };Console.WriteLine(user.Name);Console.WriteLine(user.ID);#endregion
不用实现写一个类,也可以创建一个对象,并且访问他的成员。
注意:成员名字可以自己定义。 也可以把其他成员放进来(默认名字和其他成员一样,也可以自己在指定)
自动属性
#region 自动属性 属性不能定义在方法里面int _UserId;public int UserID{ get { return _UserId; } set { _UserId = value; }}//自动属性,生成的中间语言和 先定义一个私有变量,在写他的访问器一样。public string UserNmae { get; set; }#endregion
自动属性在编译的时候生成他对应的私有字段。和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
就是在对象创建的时候初始化他的一些值。
委托
#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
委托是一个类型,和CLASS是一个级别的。
泛型
泛型类,泛型方法,泛型委托
#region 泛型DAOdao = 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。
一个传入参数,返回类型为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();Actionac2 = 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());OnePDelecustom2 = new OnePDele (stupre.GetUserNameP);Console.WriteLine(custom2("藏锋"));OutPDele custom3 = new OutPDele (stupre.GetUserNameNoP);Console.WriteLine(custom3("123", "456"));
匿名方法
delegate(){}
#region 匿名方法//委托里面添加的是一个个方法,可以写一个匿名方法给他 delegate(){} //好处 可以访问上下文变量OutPDeleunNameFun1 = new OutPDele (delegate (string s1, string s2){ return s1 + s2;});Console.WriteLine(unNameFun1("藏", "锋"));#endregion
语法糖,编译器会帮我们声明一个方法。(CLS中规定的是只有方法和字段)
好处:可以访问上下文变量。
什么代码可读性好,我感觉根据个人情况吧。编译到中间语言都一个样。
注意:匿名方法不能复制给变量 var g = delegate (string s) { Console.WriteLine(s); }
Lambda表达式
匿名方法的更加简单写法。把匿名方法的delegate换成=>。编译器也会把他生成一个方法。
无论是匿名方法还是Lambda表达式都是依赖于委托存在的。只有委托才调用他们。
定义:表达者委托或表达式树的匿名方法。 本质是一个匿名方法,有时候这种代码看不懂得时候就想想他的本质,委托调用他们。
#region LambdaOutPDelelamdFun = 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
//表达是一个委托OutPDelelamdFunD = (string s1, string s2) => s1 + s2;lamdFunD.Invoke("1", "2");
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
注意:
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;}
2,串联构造函数
调用一个构造函数的时候,他再去调用另外 一个构造函数this(若干构造函数参数)
如果要构造基类的,就是base(若干构造函数参数)
public Teacher(){}public Teacher(string p_TeacherName):this(){}public Teacher(string p_TeacherName, DateTime p_CreateDate) : base(p_CreateDate){}
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(); } }}
讲一个面试经历:有一个公司在咖啡馆面试的(其实是一个小公司,没有办公地点),所有开发语言都招,去了先根据业务设计一个算法,在填写的英文简历,通过后才面谈,刚开始讲一大推讽刺的话,在讲来我们这里可以学到好多东西,(深深的套路呀)。然后开始问专业问题,中括号怎么用的,我说 索引器,他愣了一下(后来我猜他问的是JQ里面[]属性选择器,他做前端的,可能不懂后台),后面就开始嘲讽我,然后我怼了他两句走了。见到这种人不要怂,他们也是能忽悠到一个算一个。
还有一次,他们公司开发三个人,每天加班到八点,周六也加班,然后说实行末位淘汰制,我想三个人你还淘汰啥。
迭代器
foreach的时候就是进行迭代操作,但是foreach遍历的集合必须继承IEnumerable接口实现了GetEnumerator方法(IEnumerable只有GetEnumerator()一个方法)。
foreach数组的时候,其实编译器把foreach变成for了。
迭代器的优点:对于一个较大的集合,不用一次性把数据全部加在出来。不需要遍历的时候一次加在一个。
yield:在迭代器块中用于向枚举数对象提供值或发出迭代结束信号。(迭代器中用的就是这个东西,一次返回一个值)
#region 迭代器 都要继承IEnumerable接口实现了GetEnumerator方法。Listteas = 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
//yield :在迭代器块中用于向枚举数对象提供值或发出迭代结束信号。static IEnumerable GetIterator(){ Console.WriteLine("迭代器返回了1"); yield return 1; Console.WriteLine("迭代器返回了2"); //终止迭代 //yield break; yield return 2; Console.WriteLine("迭代器返回了3"); yield return 3;}
注意事项:
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貌似已经死了)