课程6 委托、匿名方法、Lambda表达式、Linq查询表达式 上课日志1 1、委托的基本认识 提问:能不能把方法做为参数传递??? 也便是能不能声明一个能存放方法的变量呢——委托。 委托是一种数据类型,像类同样(能够声明委托类型变量),委托是用来接受方法的,经过委托能够把方法做为参数进行传递。 namespace _01委托的基本认识 { //1.定义委托类型。定义一个委托类型(委托是一种数据类型,能接受方法的数据类型),用来保存无参数,无返回值的方法 //委托最好要定义在命名空间中,和类是同一个级别 public delegate void MyDelegate();//也像定义抽象方法同样,没有实现(方法体) // public class MyClass { } //这是定义一个类 class Program { //void Run();//定义抽象方法 //string Test();//定义抽象方法 static void Main(string[] args) { //2.使用委托第二步:声明委托变量,并赋值 //声明了一个委托变量md,而且new了一个委托对象,而且把方法M1传递了进去 //即md委托保存了M1方法 //MyClass mc=new MyClass(); //MyDelegate md = new MyDelegate(M1);//第1种写法 MyDelegate md = M1;////第2种写法 //3.调用md委托的时候就至关于调用M1方法 md();//与下面那就等同 //md.Invoke();//Invoke()的做用是执行指定的委托 Console.WriteLine("ok"); Console.ReadKey(); } static void M1() { Console.WriteLine("我是一个没有参数没有返回值的方法"); } } } 2、委托的基本应用举例1 需求:假设一件事情在前面和后面要作的事情比较固定(这里假设输出“========”),可是中间要作的事情常常发生变化(好比1.要输出系统当前时间到控制台,2.要输出系统当前是星期几,3.要把系统时间写入到文本文件等) namespace _02委托的基本应用 { //定义一个委托 public delegate void middleDelegate(); public class TestClass { public void DoSomething(middleDelegate middleThing)//委托类型做为参数,也便是调用此方法要传递一个方法进来 { Console.WriteLine("=========================="); Console.WriteLine("=========================="); //执行完第二句代码,输出下系统时间 //Console.WriteLine(System.DateTime.Now.ToString()); if (middleThing != null)//委托是一个对象,就有可能为null,因此先判断下是否为null { middleThing (); } Console.WriteLine("=========================="); Console.WriteLine("=========================="); } } } namespace DelegateApp1 { class Program { static void Main(string[] args) { TestClass tc = new TestClass(); tc.DoSomething(PrintTimeToConsole); Console.ReadKey(); } public static void PrintTimeToConsole() { Console.WriteLine(System.DateTime.Now.ToString()); } static void writeTimeToFile() { File.WriteAllText("time.txt", System.DateTime.Now.ToString()); } public static void PrintWeekToConsole() { Console.WriteLine(System.DateTime.Now.DayOfWeek.ToString()); } } } 3、委托的基本应用举例2与总结 从上例能够看出委托通常是在一个方法(1)的一大段程序中间“挖个坑”,这个坑用来执行另外一个方法(2),而这个方法(2)是动态的,能够根据须要调用不一样的方法到方法(1)中。 需求:对字符串的处理常常要发生变化,好比在字符串两端加“=”、加“★”,把字符串字母所有转换为大写。 namespace _03委托的基本应用练习与总结 { //定义一个委托(委托是一种数据类型,接受方法的数据类型) public delegate string GetStringDelegate(string str); public class TestClass { public void ChangeStrings(string[] strs, GetStringDelegate GetString) { for (int i = 0; i < strs.Length; i++) { strs[i] = GetString(strs[i]);//因为对字符串的需求有不少种,因此把对字符串变化部分用委托封装成一个方法 } } } } static void Main(string[] args) { TestClass tc = new TestClass(); string[] names = new string[] { "ZhangSan", "LiSi", "WangWu", "LaoLiu" }; /*下面就能够调用含有委托的方法,若是须要在字符串两端加★,就调用ChangesStrings2方法 /若是须要把字符串的字母转换为大写,就调用ChangesStrings3方法,这样就灵活了*/ //tc.ChangeStrings(names, ChangesStrings1);//应用需求1 //tc.ChangeStrings(names, ChangesStrings2);//应用需求2 tc.ChangeStrings(names, ChangesStrings3);//应用需求3 //把变化后的字符串数组中的字符串输出 for (int i = 0; i < names.Length; i++) { Console.WriteLine(names[i]); } Console.ReadKey(); } static string ChangesStrings3(string str)//需求3:把字符串中字母转换为大写 { return str.ToUpper(); } static string ChangesStrings2(string strs)//需求2:在字符串两端加★ { return "★" + strs + "★"; } static string ChangesStrings1(string strs)//需求1:在字符串两端加★ { return "=" + strs + "="; } 委托认识的总结: 委托是一种数据类型,像类同样的一种数据类型。通常都是直接在命名空间中定义。 定义委托时,须要指明返回值类型、委托名与参数列表,这样就能肯定该类型的委托能存储(接受)什么样的方法。 使用委托 (1)声明委托变量 (2)委托是一个引用类型,就像类同样,因此当声明委托变量后,若是不赋值则该委托变量为null。因此在使用委托变量前最好作非空校验if(weituo!=null) 4、泛型委托Action与Action<T> 1、Action委托(非泛型版本) 例1 : public delegate void Mydelegate(); class Program { static void Main(string[] args) { Mydelegate md = new Mydelegate(M1); md(); //系统内置了三种委托,像这种委托就能够用系统内置委托Action,彻底不用自已写。Action委托就是一个无参数无返回值的委托(非泛型版本) Action action = M1; action(); Console.ReadKey(); } static void M1() { Console.WriteLine("ok"); } } 2、本身写泛型委托 例2:若是本身写非泛型委托:须要保存一个string类型参数,但无返回值的方法,就须要写一个委托 须要保存一个int类型参数,但无返回值的方法,就须要再写一个委托 须要保存一个bool类型参数,但无返回值的方法,就须要再另外写一个委托等等,这样的话就得写不少委托, 因而咱们就能够写泛型委托,一次性搞定。 public delegate void Mydelegate1(string msg); public delegate void Mydelegate2(int msg); public delegate void Mydelegate3(bool msg); public delegate void MyGenericdelegate<T>(T args);//这个委托就能够接受1个参数,无返回值的方法,可是这个参数数据类型能够任意 class Program { static void Main(string[] args) { MyGenericdelegate<string> md1 = M1; md1("一个参数"); MyGenericdelegate<int> md2 = M1; md2(1); MyGenericdelegate<bool> md3 = M1; md3(true); Console.ReadKey(); } static void M1(string msg) { Console.WriteLine(msg); } static void M1(int msg) { Console.WriteLine(msg); } static void M1(bool b) { Console.WriteLine(b); } } 这种状况就能够直接使用系统提供的泛型委托Action<T>。 3、Action<T>委托(泛型版本) Action<T>委托的泛型版本是一个无返回值,可是参数个数及类型能够改变的委托。 例子3: class Program { static void Main(string[] args) { Action<string> action1 = M1; action1("内置无返回值的泛型委托"); Action<int> action2 = M1; action2(12); Action<bool> action3=M1; action3(true); Console.ReadKey(); } static void M1(string msg) { Console.WriteLine(msg); } static void M1(int msg) { Console.WriteLine(msg); } static void M1(bool b) { Console.WriteLine(b); } } 系统提供了三种委托,因此之后通常不须要本身去定义委托,直接用系统内部提供的就能够 Action——Action委托的非泛型版本就是一个无参数无返回值的委托 Action<T>——Action<T>委托的泛型版本是一个无返回值,可是参数个数及类型能够改变的委托 Func<T>——Func委托只有泛型版本的,接受参数个数能够是若干个,也能够是没有参数,可是必定有返回值的方法(下节讲)
上课日志2数据库
一、补充—VS中自定义C#快速简写代码(输入简写字母后按两次Tab键自动输入) express
在VS中选择工具——>代码段管理器(语言下拉框选择Visual C#,以下图1所示),位置下面的下拉列表框再选择Visual C#,而后复制位置文本框里的路径,即找到代码段简写文件(*.snippet)。数组
而后随便复制一个(好比cw.snippet),复制的位置能够任意(注意两点:第1、不要在原来的cw.snippet位置,第2、须要新建一个文件夹(zdy)来存储复制过来的cw.snippet文件)架构
而后再对复制过来的文件修改(好比须要建立快速输入Console.ReadKey(),能够把文件名改成crk.snippet),接着打开重命名后的文件(crk.snippet)修改里面的内容(如图2所示),参照图3进行修改(修改绿色框住的4个地方便可)函数
最后单击图1下面的添加按钮,选择到自定义的文件夹(zdy)。工具
图1性能
图2学习
图3spa
Fun<T>——Func委托只有泛型版本的,接受参数个数能够是若干个,也能够是没有参数,可是必定有返回值的方法。日志
Func<TResult>这个表示没有参数,只有返回值的
Func<T,TResult>这个表示有1个参数,有返回值的
Func<T1,T2,TResult>这个表示有2个参数(前两个参数T1,T2表示参数,最后TResult返回值),有返回值的
Func<T1,T2,T3,TResult>这个表示有3个参数(前三个参数T1,T2,T3,表示参数,最后TResult返回值),有返回值的.
总之Func委托最后一个TResult表示返回值,前面的无论多少个T都是表示参数
例:
class Program
{
static void Main(string[] args)
{
#region 无参数有返回值的Fun委托
Func<int> fun1 = M1;
int n1 = fun1();
Console.WriteLine(n1);
#endregion
#region 有参数有返回值的Fun委托
Func<int, int, int, int> fun2 = M2;
int n2 = fun2(1, 2, 3);
Console.WriteLine(n2);
#endregion
Console.ReadKey();
}
static int M1()
{
return 1;
}
static int M2(int n1, int n2, int n3)
{
return n1 + n2 + n3;
}
}
多播委托就是一个委托同时绑定多个方法,多播委托也叫委托链、委托组合。
一、绑定无返回值的多个委托
class Program
{
static void Main(string[] args)
{
#region 绑定无返回值的多个委托
Action<string> action = M1;//这句话只绑定一个M1方法(绑定第一个方法不能用+=复制,由于开始action为null,因此只能用=赋值),下面再给acion绑定方法
action += M2;
action += M3;
action += M4;
action += M5;
action -= M3;//解除绑定M3方法(便是用-=赋值为解除绑定方法)
action("多播委托");
#endregion
Console.ReadKey();
}
static void M1(string msg)
{
Console.WriteLine(msg);
}
static void M2(string msg)
{
Console.WriteLine(msg);
}
static void M3(string msg)
{
Console.WriteLine(msg);
}
static void M4(string msg)
{
Console.WriteLine(msg);
}
static void M5(string msg)
{
Console.WriteLine(msg);
}
}
二、绑定有返回值的多个委托,如何获取到每一个方法的返回值
class Program
{
static void Main(string[] args)
{
#region 绑定有返回值的多个委托
Func<string, string> fc = T1;
fc += T2;
fc += T3;
string result= fc("有参数有返回值的多播委托");
Delegate[] delegates = fc.GetInvocationList();//按照调用顺序返回此多播委托的调用列表。便是有几个方法就有个几个委托,返回值为Delegate数组
for (int i = 0; i < delegates.Length; i++)//循环遍历Delegate数组便可获得每一个委托对象.这样就能够逐个委托调用,若是有返回值,能够逐个拿到
{
//delegates[i](“……”);这句不行,由于Delegate是抽象类,因此不能直接调用,须要强转为子类 //(delegates[i] as Func<string,string>)();//没有返回值就这样就能够,若是有返回值相似下一行代码就能够
string s = (delegates[i] as Func<string, string>)("获取多播委托每一个方法的返回值");
Console.WriteLine(s);
}
#endregion
Console.ReadKey();
}
static string T1(string msg)
{
return msg+"1";
}
static string T2(string msg)
{
return msg + "2";
}
static string T3(string msg)
{
return msg + "3";
}
}
一、匿名类
static void Main(string[] args)
{
#region 匿名类
var Anon = new { Name = "小明", Age = 3, Sex = '男' };
Console.WriteLine("个人名字是:{0},性别为{1},年龄是{2}", Anon.Name, Anon.Sex, Anon.Age);
Console.ReadKey();
#endregion
}
二、匿名方法
匿名方法,便是没有名字的方法,不能直接在类中定义,而是在给委托变量赋值的时候,须要赋值一个方法,此时能够“现作现卖”,定义一个匿名方法传递给该委托。
匿名方法关键字delegate,delegate后的括号写方法参数,{ }里面写方法体,这是一个赋值语句因此最后须要分号。
例1:
#region 匿名方法(无参数无返回值)
//若是存在一个已定义好的M1方法,则直接能够把该方法赋给委托变量md
// Action md = M1;
//若是不存在已定义好的方法,则可使用匿名方法赋给委托变量,即现定义一个方法给委托变量
Action md = delegate()
{
Console.WriteLine("ok");
};
md();//调用匿名方法
Console.ReadKey();
#endregion
static void M1()
{
Console.WriteLine("ok");
}
例2:
#region 有参数无返回值的匿名方法
Action<string> md2 = delegate(string msg)
{
Console.WriteLine("Hello!" + msg);
};
md2("你们好!");
Console.ReadKey();
#endregion
例3:
#region 有参数有返回值的匿名方法
Func<int,int,int,int> ad = delegate(int n1, int n2, int n3)//提问:这里若是不采用匿名方法怎么写
{
return n1 + n2 + n3;
};
int result = ad(12, 10, 8);
Console.WriteLine(result);
Console.ReadKey();
#endregion
一、Lambda介绍
“Lambda 表达式”(lambda expression)就是一个匿名函数(匿名方法),Lambda表达式基于数学中的λ演算得名。
Lambda运算符:全部的lambda表达式都是用新的lambda运算符 " => ",能够叫他:“转到”或者 “成为”,读做 “goes to”。运算符将表达式分为两部分,左边指定输入参数,右边是lambda的主体(方法体)。
lambda表达式:
一个参数:param=>expr
多个参数:(param-list)=>expr
二、输入参数与表达式或语句块
输入参数:在Lambda表达式中,输入参数是Lambda运算符的左边部分。它包含参数的数量能够为0、1或者多个。只有当输入参数为1时,Lambda表达式左边的一对小括弧才能够省略。输入参数的数量大于或者等于2时,Lambda表达式左边的一对小括弧中的多个参数之间使用逗号(,)分割。
表达式或语句块:多个Lambda表达式能够构成Lambda语句块。语句块是放到运算符的右边,做为Lambda的主体。语句块中能够包含多条语句,而且能够包含循环、方法调用和if语句等。语句块必须被"{"和"}"包围。
例1:无参数、表达式
#region 无参数
//Action a = () => { Console.WriteLine("This is a Lambda expression."); };
Action a= ()=>Console.WriteLine("This is a Lambda expression.");
a();
Console.ReadKey();
#endregion
因为上述Lambda表达式的输入参数的数量为0,所以,该Lambda表达式的左边部分的一对小括弧不能被省略。右边是一个表达式
例2:1个参数状况、语句块
#region 1个参数
Action<int> b = m => { int n = m * 2; Console.WriteLine(n); };//此处参数m的括号能够省略
b(2);
#endregion
上述Lambda表达式的输入参数省略了一对小括弧,它与“(m)=> { int n = m * 2; Console.WriteLine(n); };是等效的。右边是语句块,那么该语句块必须被"{"和"}"包围,还有return语句不能省略花括号。
例3:多个参数、语句块
#region 多个参数
Action<int,int> c = (m,n) => { int s = m * n; Console.WriteLine(s); };//此处参数的括号不能省略
c(2,3);
#endregion
1、匿名方法与Lambda的替换
static void Main(string[] args)
{
//Func<int, int, int, int> ad = M2;
#region 有参数有返回值的匿名方法
Func<int, int, int, int> ad = delegate(int n1, int n2, int n3)//该匿名方法实际上与下面的有名方法M2同样
{
return n1 + n2 + n3;
};
int result = ad(12, 10, 8);
Console.WriteLine(result);
#endregion
#region 有参数有返回值的lambda表达式
Func<int, int, int, int> ad2 = (x, y, z) => { return x + y + z; };
int r = ad2(10, 20, 30);
Console.WriteLine(r);
#endregion
Console.ReadKey();
}
static int M2(int n1, int n2, int n3)
{
return n1 + n2 + n3;
}
说明:匿名方法、lambda表达式运行时最终都会编译成方法
2、定义一个能接收参数个数可变且有返回值的委托
public delegate int Adddelegate(params int[] arr);//定义一个能接收参数个数可变且有返回值的委托
static void Main(string[] args)
{
#region 接收参数个数可变的lambda表达式
Adddelegate ad = (arr) =>
{
for (int i = 0; i < arr.Length; i++)
{
Console.WriteLine(arr[i]);
}
return arr.Sum();
};
int x = ad(1, 2, 3, 4, 5);
Console.WriteLine(x);
Console.ReadKey();
#endregion
}
3、lambda表达式在泛型集合中的应用
示例:打印出泛型集合中大于6的元素。
static void Main(string[] args)
{
List<int> list = new List<int>() { 1, 2, 3, 4, 5, 6, 7, 89, 10, 11, 12, 13, 14, 15 };
//第一种写法本身定义方法
IEnumerable<int> ie = list.Where(cs); //var
//第二种写法用匿名方法
//IEnumerable<int> ie = list.Where(delegate(int n)
//{
// return n > 6;
//}
// );//where里面须要一个方法做为参数(有1个int类型参数,返回值为bool类型)
//第三种写法使用lambda表达式
//IEnumerable<int> ie = list.Where(n => { return n > 6; });
foreach (var item in ie)
{
Console.WriteLine(item);
}
Console.ReadKey();
#endregion
}
static bool cs(int n)
{
return n > 6;
}
LINQ的全称是Language Integrated Query,中文译成“语言集成查询”。LINQ是一种查询技术,有LINQ to SQL、LINQ to Object、LINQ to ADO.NET、LINQ to XML、LINQ to EF等。
LINQ与直接SQL语句比较:
(1)SQL数据库技术是一门相对比较复杂深奥的技术,不是人人都懂,而LINQ To SQL比较简单(实际上底层都对数据库操做进行了封装,架构了数据访问层)
(2)直接写SQL语句,若是有问题,只有到运行时才知道
(3)LINQ To SQL能够很好的防止注入式攻击
(4)Linq是面向对象的查询,主要在程序内部使用(好比查找所需的数据),比使用DataReader读取数据等方便多了;直接SQL是面向关系型数据库的查询
(5)从运行效率上来讲性能不如直接SQL语句,可是开发效率提升了。
要学习LINQ首先就要学习LINQ查询表达式。
LINQ的查询由3基本部分组成:获取数据源,建立查询,执行查询。
// 1.获取数据源
List<int> numbers = new List<int>() { 5, 4, 1, 3, 9, 8, 6, 7, 2, 0 };
// 2.建立查询
var numQuery = from num in numbers
where num % 2 == 0
select num*10;
//与SQL语句比较下
//string ql=”select StuName from Student where ID>1”
// 3.执行查询
foreach (var num in numQuery)
{
Console.WriteLine(num);
}
在 LINQ 中,查询的执行与查询自己大相径庭;换句话说,若是只是建立查询变量,则不会检索任何数据。
Linq的数据源要求必须实现IEnumerable或IEnumerable<T>接口,数组隐式支持这个接口。numQuery叫作查询变量,它存储了一个查询表达式。注意,声明查询变量并不会执行查询,真正的执行查询延迟到了foreach语句中。
查询表达式必须以from子句开头,以select或group子句结束。第一个from子句和最后一个select子句或group子句之间,能够包含一个或多个where子句、let子句、join子 句、orderby子句和group子句,甚至还能够是from子句。它包括8个基本子句,具体说明以下所示。
●from子句:指定查询操做的数据源和范围变量。
●select子句:指定查询结果的类型和表现形式。
●where子句:指定筛选元素的逻辑条件。
●let子句:引入用来临时保存查询表达式中的子表达式结果的范围变量。
●orderby子句:对查询结果进行排序操做,包括升序和降序。
●group子句:对查询结果进行分组。
●into子句:提供一个临时标识符。join子句、group子句或select子句能够经过该标识符引用查询操做中的结果。
●join子句:链接多个用于查询操做的数据源。
建立一个LINQ表达式必需要以from子句开头。
例1:单个Form子句
string[] values = { "中国", "日本", "美国", "菲律宾", "越南" };
//查询包含“国”的字符串
// IndexOf查询参数字符串在父串中首次出现的位置,没有返回-1
var valueQuery = from v in values
where v.IndexOf("国") > 0
select v;
foreach (var v in valueQuery)
{
Console.WriteLine(v);
}
Console.ReadKey();
在这个LINQ表达式的from子句中,v叫作范围变量,values是数据源。v的做用域存在于当前的LINQ表达式,表达式之外不能访问这个变量。where用来筛选元素,select用于输出元素。这里的范围变量v和foreach语句中的隐式变量v均可以由编译器推断出其类型。
例2:复合Form子句
在查询数据源中,元素的属性是一个集合时,可使用复合from子句对这个属性集合查询。好比,一个客户,可能有多个电话。
class CustomerInfo
{
public string Name { get; set; }
public int Age { get; set; }
public List<string> TelTable { get; set; }
}
static void Main(string[] args)
{
formExpDemo();
Console.ReadKey();
}
static void formExpDemo()
{
List<CustomerInfo> customers = new List<CustomerInfo> {
new CustomerInfo{ Name="欧阳晓晓", Age=35, TelTable=new List<string>{"1330708****","1330709****"}},
new CustomerInfo{ Name="上官飘飘", Age=17, TelTable=new List<string>{"1592842****","1592843****"}},
new CustomerInfo{ Name="诸葛菲菲", Age=23, TelTable=new List<string>{"1380524****","1380525****"}}
};
//查询包含电话号码1592842****的客户
var query = from CustomerInfo ci in customers//ci
from tel in ci.TelTable
where tel.IndexOf("1592842****") > -1
select ci;
foreach (var ci in query)
{
Console.WriteLine("姓名:{0} 年龄:{1}", ci.Name, ci.Age);
foreach (var tel in ci.TelTable)
{
Console.WriteLine(" 电话:{0}", tel);
}
}
}
1、where子句
where子句的做用就是筛选元素,除了开始和结束位置,where子句几乎能够出如今LINQ表达式的任意位置。一个LINQ表达式中能够有where子句,也能够没有;能够有一个,能够有多个;多个where子句之间的关系至关于逻辑“与”,每一个where子句能够包含1个或多个逻辑表达式,这些条件成为“谓词”,多个谓词之间用布尔运算符隔开,好比逻辑“与”用&&,逻辑“或”用||,而不是用SQL中的AND或OR。
class CustomerInfo
{
public string Name { get; set; }
public int Age { get; set; }
public string Tel { get; set; }
}
List<CustomerInfo> clist = new List<CustomerInfo> {
new CustomerInfo{ Name="欧阳晓晓", Age=35, Tel ="1330708****"},
new CustomerInfo{ Name="上官飘飘", Age=17, Tel ="1592842****"},
new CustomerInfo{ Name="令狐冲", Age=23, Tel ="1380524****"}
};
//查询名字是3个字或者姓“令”的,但年龄大于20的客户
var query = from customer in clist
where (customer.Name.Length == 3 || customer.Name.Substring(0, 1) == "令") && customer.Age > 20
select customer;
foreach (var ci in query)
{
Console.WriteLine("姓名:{0} 年龄:{1} 电话:{2}", ci.Name, ci.Age, ci.Tel);
}
2、select子句
例1:最简单的select就是直接输出from子句创建的那个范围变量:
#region 简单的select示例
int[] arr = new int[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
var query = from n in arr
select n; //select n*10;对象.属性
#endregion
例2:对查询结果进行投影(转换)。下面查询表达式查询arr数组中的每个元素,查询结果转换为一个集合对象的两个属性值:ID和Name,它在select子句中由匿名对象初始化器建立。每个对象的ID属性的值是当前元素的值、Name属性的值为元素的值的字符串的表现形式。
#region 对查询结果进行投影(转换)
int[] arr = new int[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
var query = from n in arr
select new
{
ID = n,
Name = n.ToString()
};
foreach (var item in query)
{
Console.WriteLine(item.ID+" 张"+item.Name);
}
Console.ReadKey();
#endregion
1、Group子句
LINQ表达式必须以from子句开头,以select或group子句结束,因此除了使用select子句外,也可使用guoup子句来返回元素分组后的结果。group子句用来将查询结果分组,并返回一对象序列。这些对象包含零个或更多个与该组的key值匹配的项。
注意:每个分组都不是单个元素,而是一个序列(也属于集合)。序列的元素类型为IGrouping<TKey,TElement>(必须以Group子句结束的LINQ表达式,分组结果类型才为序列,序列的元素类型为IGrouping<TKey,TElement>)
List<CustomerInfo> clist = new List<CustomerInfo> {
new CustomerInfo{ Name="欧阳晓晓", Age=35, Tel ="1330708****"},
new CustomerInfo{ Name="上官飘飘", Age=17, Tel ="1592842****"},
new CustomerInfo{ Name="欧阳锦鹏", Age=35, Tel ="1330708****"},
new CustomerInfo{ Name="上官无忌", Age=23, Tel ="1380524****"}
};
//按照名字的前2个字进行分组
var query = from customer in clist
group customer by customer.Name.Substring(0, 2);
//foreach (IGrouping<string, CustomerInfo> group in query)
foreach (var group in query)
{
Console.WriteLine("分组键:{0}", group.Key);
foreach (var ci in group)
{
Console.WriteLine("姓名:{0} 电话:{1}", ci.Name, ci.Tel);
}
Console.WriteLine("***************************************");
}
2、into子句
into子句能够用来建立一个临时标识符,将group、join或select子句的结果存储到这个标识符中。
int[] arr = new int[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
var query = from n in arr
where n > 1 && n < 6
group n by n % 2 into g
from sn in g
select sn;
foreach (var item in query)
{
Console.WriteLine(item);
}
3、orderby子句(中间无空格)
orderby子句可以使返回的查询结果按升序或者降序排序。升序由关键字ascending指定,而降序由关键字descending指定。
注意:orderby子句默认排序方式为升序。
示例1:
int[] arr = new int[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
var query = from n in arr
where n > 1 && n < 6
orderby n descending
select n;
示例2:
int[] arr = new int[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
var query = from n in arr
where n > 1 && n < 6
orderby n % 2 ascending, n descending
select n;
n%2:按照升序排序;n:按照降序排序。
注意:n%2排序关键字优先级大于n排序关键字。所以,该查询表达式的结果首先按照n%2排序关键字升序排序,而后再按照n排序关键字降序排序。第一个排序关键字后的"ascending"能够省略。由于默认排序方式为升序。
1、let子句
let子句用于在LINQ表达式中存储子表达式的计算结果。即let子句建立一个范围变量来存储结果,变量被建立后,不能修改或把其余表达式的结果从新赋值给它。此范围变量能够在后续的LINQ子句中使用。
List<CustomerInfo> clist = new List<CustomerInfo> {
new CustomerInfo{ Name="欧阳晓晓", Age=35, Tel ="1330708****"},
new CustomerInfo{ Name="上官飘飘", Age=17, Tel ="1592842****"},
new CustomerInfo{ Name="郭靖", Age=17, Tel ="1330708****"},
new CustomerInfo{ Name="黄蓉", Age=17, Tel ="1300524****"}
};
//姓“郭”或“黄”的客户
var query = from customer in clist
let g = customer.Name.Substring(0, 1)
where g == "郭" || g == "黄"
select customer;
foreach (var ci in query)
{
Console.WriteLine("姓名:{0} 年龄:{1} 电话:{2}", ci.Name, ci.Age, ci.Tel);
}
//where customer.Name.Substring(0, 1) == "郭" || customer.Name.Substring(0, 1) == "黄"
2、join子句
join子句用来链接两个数据源,即设置两个数据源之间的关系。join子句支持如下3种常见联接方式。
内部联接:要求两个数据源都必须存在相同的值,即两个数据源都必须存在知足联接关系的元素。相似于SQL语句中的inner join子句。
分组联接:包含into子句的join子句。
左外部联接:元素的连接关系必须知足联接中的左数据源,相似于SQL语句中的left join子句。
内部链接示例:
int[] arra = new int[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
int[] arrb = new int[] { 0, 2, 4, 6, 8 };
var query = from a in arra
where a < 7
join b in arrb on a equals b
select a;
上课日志2
一、补充—VS中自定义C#快速简写代码(输入简写字母后按两次Tab键自动输入)
在VS中选择工具——>代码段管理器(语言下拉框选择Visual C#,以下图1所示),位置下面的下拉列表框再选择Visual C#,而后复制位置文本框里的路径,即找到代码段简写文件(*.snippet)。
而后随便复制一个(好比cw.snippet),复制的位置能够任意(注意两点:第1、不要在原来的cw.snippet位置,第2、须要新建一个文件夹(zdy)来存储复制过来的cw.snippet文件)
而后再对复制过来的文件修改(好比须要建立快速输入Console.ReadKey(),能够把文件名改成crk.snippet),接着打开重命名后的文件(crk.snippet)修改里面的内容(如图2所示),参照图3进行修改(修改绿色框住的4个地方便可)
最后单击图1下面的添加按钮,选择到自定义的文件夹(zdy)。
图1
图2
图3
Fun<T>——Func委托只有泛型版本的,接受参数个数能够是若干个,也能够是没有参数,可是必定有返回值的方法。
Func<TResult>这个表示没有参数,只有返回值的
Func<T,TResult>这个表示有1个参数,有返回值的
Func<T1,T2,TResult>这个表示有2个参数(前两个参数T1,T2表示参数,最后TResult返回值),有返回值的
Func<T1,T2,T3,TResult>这个表示有3个参数(前三个参数T1,T2,T3,表示参数,最后TResult返回值),有返回值的.
总之Func委托最后一个TResult表示返回值,前面的无论多少个T都是表示参数
例:
class Program
{
static void Main(string[] args)
{
#region 无参数有返回值的Fun委托
Func<int> fun1 = M1;
int n1 = fun1();
Console.WriteLine(n1);
#endregion
#region 有参数有返回值的Fun委托
Func<int, int, int, int> fun2 = M2;
int n2 = fun2(1, 2, 3);
Console.WriteLine(n2);
#endregion
Console.ReadKey();
}
static int M1()
{
return 1;
}
static int M2(int n1, int n2, int n3)
{
return n1 + n2 + n3;
}
}
多播委托就是一个委托同时绑定多个方法,多播委托也叫委托链、委托组合。
一、绑定无返回值的多个委托
class Program
{
static void Main(string[] args)
{
#region 绑定无返回值的多个委托
Action<string> action = M1;//这句话只绑定一个M1方法(绑定第一个方法不能用+=复制,由于开始action为null,因此只能用=赋值),下面再给acion绑定方法
action += M2;
action += M3;
action += M4;
action += M5;
action -= M3;//解除绑定M3方法(便是用-=赋值为解除绑定方法)
action("多播委托");
#endregion
Console.ReadKey();
}
static void M1(string msg)
{
Console.WriteLine(msg);
}
static void M2(string msg)
{
Console.WriteLine(msg);
}
static void M3(string msg)
{
Console.WriteLine(msg);
}
static void M4(string msg)
{
Console.WriteLine(msg);
}
static void M5(string msg)
{
Console.WriteLine(msg);
}
}
二、绑定有返回值的多个委托,如何获取到每一个方法的返回值
class Program
{
static void Main(string[] args)
{
#region 绑定有返回值的多个委托
Func<string, string> fc = T1;
fc += T2;
fc += T3;
string result= fc("有参数有返回值的多播委托");
Delegate[] delegates = fc.GetInvocationList();//按照调用顺序返回此多播委托的调用列表。便是有几个方法就有个几个委托,返回值为Delegate数组
for (int i = 0; i < delegates.Length; i++)//循环遍历Delegate数组便可获得每一个委托对象.这样就能够逐个委托调用,若是有返回值,能够逐个拿到
{
//delegates[i](“……”);这句不行,由于Delegate是抽象类,因此不能直接调用,须要强转为子类 //(delegates[i] as Func<string,string>)();//没有返回值就这样就能够,若是有返回值相似下一行代码就能够
string s = (delegates[i] as Func<string, string>)("获取多播委托每一个方法的返回值");
Console.WriteLine(s);
}
#endregion
Console.ReadKey();
}
static string T1(string msg)
{
return msg+"1";
}
static string T2(string msg)
{
return msg + "2";
}
static string T3(string msg)
{
return msg + "3";
}
}
一、匿名类
static void Main(string[] args)
{
#region 匿名类
var Anon = new { Name = "小明", Age = 3, Sex = '男' };
Console.WriteLine("个人名字是:{0},性别为{1},年龄是{2}", Anon.Name, Anon.Sex, Anon.Age);
Console.ReadKey();
#endregion
}
二、匿名方法
匿名方法,便是没有名字的方法,不能直接在类中定义,而是在给委托变量赋值的时候,须要赋值一个方法,此时能够“现作现卖”,定义一个匿名方法传递给该委托。
匿名方法关键字delegate,delegate后的括号写方法参数,{ }里面写方法体,这是一个赋值语句因此最后须要分号。
例1:
#region 匿名方法(无参数无返回值)
//若是存在一个已定义好的M1方法,则直接能够把该方法赋给委托变量md
// Action md = M1;
//若是不存在已定义好的方法,则可使用匿名方法赋给委托变量,即现定义一个方法给委托变量
Action md = delegate()
{
Console.WriteLine("ok");
};
md();//调用匿名方法
Console.ReadKey();
#endregion
static void M1()
{
Console.WriteLine("ok");
}
例2:
#region 有参数无返回值的匿名方法
Action<string> md2 = delegate(string msg)
{
Console.WriteLine("Hello!" + msg);
};
md2("你们好!");
Console.ReadKey();
#endregion
例3:
#region 有参数有返回值的匿名方法
Func<int,int,int,int> ad = delegate(int n1, int n2, int n3)//提问:这里若是不采用匿名方法怎么写
{
return n1 + n2 + n3;
};
int result = ad(12, 10, 8);
Console.WriteLine(result);
Console.ReadKey();
#endregion
一、Lambda介绍
“Lambda 表达式”(lambda expression)就是一个匿名函数(匿名方法),Lambda表达式基于数学中的λ演算得名。
Lambda运算符:全部的lambda表达式都是用新的lambda运算符 " => ",能够叫他:“转到”或者 “成为”,读做 “goes to”。运算符将表达式分为两部分,左边指定输入参数,右边是lambda的主体(方法体)。
lambda表达式:
一个参数:param=>expr
多个参数:(param-list)=>expr
二、输入参数与表达式或语句块
输入参数:在Lambda表达式中,输入参数是Lambda运算符的左边部分。它包含参数的数量能够为0、1或者多个。只有当输入参数为1时,Lambda表达式左边的一对小括弧才能够省略。输入参数的数量大于或者等于2时,Lambda表达式左边的一对小括弧中的多个参数之间使用逗号(,)分割。
表达式或语句块:多个Lambda表达式能够构成Lambda语句块。语句块是放到运算符的右边,做为Lambda的主体。语句块中能够包含多条语句,而且能够包含循环、方法调用和if语句等。语句块必须被"{"和"}"包围。
例1:无参数、表达式
#region 无参数
//Action a = () => { Console.WriteLine("This is a Lambda expression."); };
Action a= ()=>Console.WriteLine("This is a Lambda expression.");
a();
Console.ReadKey();
#endregion
因为上述Lambda表达式的输入参数的数量为0,所以,该Lambda表达式的左边部分的一对小括弧不能被省略。右边是一个表达式
例2:1个参数状况、语句块
#region 1个参数
Action<int> b = m => { int n = m * 2; Console.WriteLine(n); };//此处参数m的括号能够省略
b(2);
#endregion
上述Lambda表达式的输入参数省略了一对小括弧,它与“(m)=> { int n = m * 2; Console.WriteLine(n); };是等效的。右边是语句块,那么该语句块必须被"{"和"}"包围,还有return语句不能省略花括号。
例3:多个参数、语句块
#region 多个参数
Action<int,int> c = (m,n) => { int s = m * n; Console.WriteLine(s); };//此处参数的括号不能省略
c(2,3);
#endregion
1、匿名方法与Lambda的替换
static void Main(string[] args)
{
//Func<int, int, int, int> ad = M2;
#region 有参数有返回值的匿名方法
Func<int, int, int, int> ad = delegate(int n1, int n2, int n3)//该匿名方法实际上与下面的有名方法M2同样
{
return n1 + n2 + n3;
};
int result = ad(12, 10, 8);
Console.WriteLine(result);
#endregion
#region 有参数有返回值的lambda表达式
Func<int, int, int, int> ad2 = (x, y, z) => { return x + y + z; };
int r = ad2(10, 20, 30);
Console.WriteLine(r);
#endregion
Console.ReadKey();
}
static int M2(int n1, int n2, int n3)
{
return n1 + n2 + n3;
}
说明:匿名方法、lambda表达式运行时最终都会编译成方法
2、定义一个能接收参数个数可变且有返回值的委托
public delegate int Adddelegate(params int[] arr);//定义一个能接收参数个数可变且有返回值的委托
static void Main(string[] args)
{
#region 接收参数个数可变的lambda表达式
Adddelegate ad = (arr) =>
{
for (int i = 0; i < arr.Length; i++)
{
Console.WriteLine(arr[i]);
}
return arr.Sum();
};
int x = ad(1, 2, 3, 4, 5);
Console.WriteLine(x);
Console.ReadKey();
#endregion
}
3、lambda表达式在泛型集合中的应用
示例:打印出泛型集合中大于6的元素。
static void Main(string[] args)
{
List<int> list = new List<int>() { 1, 2, 3, 4, 5, 6, 7, 89, 10, 11, 12, 13, 14, 15 };
//第一种写法本身定义方法
IEnumerable<int> ie = list.Where(cs); //var
//第二种写法用匿名方法
//IEnumerable<int> ie = list.Where(delegate(int n)
//{
// return n > 6;
//}
// );//where里面须要一个方法做为参数(有1个int类型参数,返回值为bool类型)
//第三种写法使用lambda表达式
//IEnumerable<int> ie = list.Where(n => { return n > 6; });
foreach (var item in ie)
{
Console.WriteLine(item);
}
Console.ReadKey();
#endregion
}
static bool cs(int n)
{
return n > 6;
}
LINQ的全称是Language Integrated Query,中文译成“语言集成查询”。LINQ是一种查询技术,有LINQ to SQL、LINQ to Object、LINQ to ADO.NET、LINQ to XML、LINQ to EF等。
LINQ与直接SQL语句比较:
(1)SQL数据库技术是一门相对比较复杂深奥的技术,不是人人都懂,而LINQ To SQL比较简单(实际上底层都对数据库操做进行了封装,架构了数据访问层)
(2)直接写SQL语句,若是有问题,只有到运行时才知道
(3)LINQ To SQL能够很好的防止注入式攻击
(4)Linq是面向对象的查询,主要在程序内部使用(好比查找所需的数据),比使用DataReader读取数据等方便多了;直接SQL是面向关系型数据库的查询
(5)从运行效率上来讲性能不如直接SQL语句,可是开发效率提升了。
要学习LINQ首先就要学习LINQ查询表达式。
LINQ的查询由3基本部分组成:获取数据源,建立查询,执行查询。
// 1.获取数据源
List<int> numbers = new List<int>() { 5, 4, 1, 3, 9, 8, 6, 7, 2, 0 };
// 2.建立查询
var numQuery = from num in numbers
where num % 2 == 0
select num*10;
//与SQL语句比较下
//string ql=”select StuName from Student where ID>1”
// 3.执行查询
foreach (var num in numQuery)
{
Console.WriteLine(num);
}
在 LINQ 中,查询的执行与查询自己大相径庭;换句话说,若是只是建立查询变量,则不会检索任何数据。
Linq的数据源要求必须实现IEnumerable或IEnumerable<T>接口,数组隐式支持这个接口。numQuery叫作查询变量,它存储了一个查询表达式。注意,声明查询变量并不会执行查询,真正的执行查询延迟到了foreach语句中。
查询表达式必须以from子句开头,以select或group子句结束。第一个from子句和最后一个select子句或group子句之间,能够包含一个或多个where子句、let子句、join子 句、orderby子句和group子句,甚至还能够是from子句。它包括8个基本子句,具体说明以下所示。
●from子句:指定查询操做的数据源和范围变量。
●select子句:指定查询结果的类型和表现形式。
●where子句:指定筛选元素的逻辑条件。
●let子句:引入用来临时保存查询表达式中的子表达式结果的范围变量。
●orderby子句:对查询结果进行排序操做,包括升序和降序。
●group子句:对查询结果进行分组。
●into子句:提供一个临时标识符。join子句、group子句或select子句能够经过该标识符引用查询操做中的结果。
●join子句:链接多个用于查询操做的数据源。
建立一个LINQ表达式必需要以from子句开头。
例1:单个Form子句
string[] values = { "中国", "日本", "美国", "菲律宾", "越南" };
//查询包含“国”的字符串
// IndexOf查询参数字符串在父串中首次出现的位置,没有返回-1
var valueQuery = from v in values
where v.IndexOf("国") > 0
select v;
foreach (var v in valueQuery)
{
Console.WriteLine(v);
}
Console.ReadKey();
在这个LINQ表达式的from子句中,v叫作范围变量,values是数据源。v的做用域存在于当前的LINQ表达式,表达式之外不能访问这个变量。where用来筛选元素,select用于输出元素。这里的范围变量v和foreach语句中的隐式变量v均可以由编译器推断出其类型。
例2:复合Form子句
在查询数据源中,元素的属性是一个集合时,可使用复合from子句对这个属性集合查询。好比,一个客户,可能有多个电话。
class CustomerInfo
{
public string Name { get; set; }
public int Age { get; set; }
public List<string> TelTable { get; set; }
}
static void Main(string[] args)
{
formExpDemo();
Console.ReadKey();
}
static void formExpDemo()
{
List<CustomerInfo> customers = new List<CustomerInfo> {
new CustomerInfo{ Name="欧阳晓晓", Age=35, TelTable=new List<string>{"1330708****","1330709****"}},
new CustomerInfo{ Name="上官飘飘", Age=17, TelTable=new List<string>{"1592842****","1592843****"}},
new CustomerInfo{ Name="诸葛菲菲", Age=23, TelTable=new List<string>{"1380524****","1380525****"}}
};
//查询包含电话号码1592842****的客户
var query = from CustomerInfo ci in customers//ci
from tel in ci.TelTable
where tel.IndexOf("1592842****") > -1
select ci;
foreach (var ci in query)
{
Console.WriteLine("姓名:{0} 年龄:{1}", ci.Name, ci.Age);
foreach (var tel in ci.TelTable)
{
Console.WriteLine(" 电话:{0}", tel);
}
}
}
1、where子句
where子句的做用就是筛选元素,除了开始和结束位置,where子句几乎能够出如今LINQ表达式的任意位置。一个LINQ表达式中能够有where子句,也能够没有;能够有一个,能够有多个;多个where子句之间的关系至关于逻辑“与”,每一个where子句能够包含1个或多个逻辑表达式,这些条件成为“谓词”,多个谓词之间用布尔运算符隔开,好比逻辑“与”用&&,逻辑“或”用||,而不是用SQL中的AND或OR。
class CustomerInfo
{
public string Name { get; set; }
public int Age { get; set; }
public string Tel { get; set; }
}
List<CustomerInfo> clist = new List<CustomerInfo> {
new CustomerInfo{ Name="欧阳晓晓", Age=35, Tel ="1330708****"},
new CustomerInfo{ Name="上官飘飘", Age=17, Tel ="1592842****"},
new CustomerInfo{ Name="令狐冲", Age=23, Tel ="1380524****"}
};
//查询名字是3个字或者姓“令”的,但年龄大于20的客户
var query = from customer in clist
where (customer.Name.Length == 3 || customer.Name.Substring(0, 1) == "令") && customer.Age > 20
select customer;
foreach (var ci in query)
{
Console.WriteLine("姓名:{0} 年龄:{1} 电话:{2}", ci.Name, ci.Age, ci.Tel);
}
2、select子句
例1:最简单的select就是直接输出from子句创建的那个范围变量:
#region 简单的select示例
int[] arr = new int[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
var query = from n in arr
select n; //select n*10;对象.属性
#endregion
例2:对查询结果进行投影(转换)。下面查询表达式查询arr数组中的每个元素,查询结果转换为一个集合对象的两个属性值:ID和Name,它在select子句中由匿名对象初始化器建立。每个对象的ID属性的值是当前元素的值、Name属性的值为元素的值的字符串的表现形式。
#region 对查询结果进行投影(转换)
int[] arr = new int[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
var query = from n in arr
select new
{
ID = n,
Name = n.ToString()
};
foreach (var item in query)
{
Console.WriteLine(item.ID+" 张"+item.Name);
}
Console.ReadKey();
#endregion
1、Group子句
LINQ表达式必须以from子句开头,以select或group子句结束,因此除了使用select子句外,也可使用guoup子句来返回元素分组后的结果。group子句用来将查询结果分组,并返回一对象序列。这些对象包含零个或更多个与该组的key值匹配的项。
注意:每个分组都不是单个元素,而是一个序列(也属于集合)。序列的元素类型为IGrouping<TKey,TElement>(必须以Group子句结束的LINQ表达式,分组结果类型才为序列,序列的元素类型为IGrouping<TKey,TElement>)
List<CustomerInfo> clist = new List<CustomerInfo> {
new CustomerInfo{ Name="欧阳晓晓", Age=35, Tel ="1330708****"},
new