C#-LINQ

隐式类型

 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<int> listInt = new List<int>() { 1, 2, 3, 4 };


//无论是值类型还是引用类型使用的时候都要初始化 赋值。
//值类型一般有默认值0
//引用类型为NULL
Student 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<Teacher> dao = new DAO<Teacher>();
Teacher teac = new Teacher();
var pros1 = dao.GetAllPro();
var pros2 = dao.GetAllPro(teac);
Student stRef = new Student();
var typeName = dao.GetTypeName<Student>(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<int> pStu = new Predicate<int>(stupre.CheUsreID);
Console.WriteLine(pStu(stupre.ID));

//0-16个参数     17中方法重载
Action ac1 = new Action(stupre.GetUserNameNoP);
ac1();
Action<string> ac2 = new Action<string>(stupre.GetUserNameNoP);
ac2("123");

//最后一个参数为返回参数out  协变   ,前面可以放0-16个参数
Func<string> fun1 = new Func<string>(stupre.ToString);
fun1();
Func<int, int> fun2 = new Func<int, int>(stupre.GetUserId3);
Console.WriteLine(fun2(5));
#endregion
三种框架泛型委托代码
//自定义泛型委托
NoInOutDele<int> custom1 = new NoInOutDele<int>(stupre.GetUserNameP);
Console.WriteLine(custom1());
OnePDele<string> custom2 = new OnePDele<string>(stupre.GetUserNameP);
Console.WriteLine(custom2("藏锋"));
OutPDele<string, string, string> custom3 = new OutPDele<string, string, string>(stupre.GetUserNameNoP);
Console.WriteLine(custom3("123", "456"));
自定义泛型委托

 

匿名方法

   delegate(){}

#region 匿名方法
//委托里面添加的是一个个方法,可以写一个匿名方法给他   delegate(){} 
//好处 可以访问上下文变量
OutPDele<string, string, string> unNameFun1 = new OutPDele<string, string, string>(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 Lambda
OutPDele<string, string, string> lamdFun = new OutPDele<string, string, string>((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<string> lamdFun1 = new OnePDele<string>(s => { return s; });
#endregion
View Code
//表达是一个委托
OutPDele<string, string, string> 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<Teacher> teas = new List<Teacher>();
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<int> 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。这种是一个语法糖。

 

源码下载

推荐文章:http://www.cnblogs.com/liulun/archive/2013/02/26/2909985.html

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

posted @ 2017-08-22 00:50  西伯利亚的狼  阅读(8108)  评论(0编辑  收藏  举报