课程6 委托、匿名方法、Lambda表达式、Linq查询表达式
上课日志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("我是一个没有参数没有返回值的方法");
}
}
}
二、委托的基本应用举例1
需求:假设一件事情在前面和后面要做的事情比较固定(这里假设输出“========”),但是中间要做的事情经常发生变化(比如1.要输出系统当前时间到控制台,.要输出系统当前是星期几,.要把系统时间写入到文本文件等)
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());
}
}
}
三、委托的基本应用举例2与总结
从上例可以看出委托一般是在一个方法()的一大段程序中间“挖个坑”,这个坑用来执行另一个方法(),而这个方法()是动态的,可以根据需要调用不同的方法到方法()中。
需求:对字符串的处理经常要发生变化,比如在字符串两端加“=”、加“★”,把字符串字母全部转换为大写。
namespace _03委托的基本应用练习与总结
{
//定义一个委托(委托是一种数据类型,接受方法的数据类型)
public delegate string GetStringDelegate(string str); public class TestClass
{
public void ChangeStrings(string[] strs, GetStringDelegate GetString)
{
for (int i = ; 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 = ; 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 + "=";
}
委托认识的总结:
委托是一种数据类型,像类一样的一种数据类型。一般都是直接在命名空间中定义。
定义委托时,需要指明返回值类型、委托名与参数列表,这样就能确定该类型的委托能存储(接受)什么样的方法。
使用委托
()声明委托变量
()委托是一个引用类型,就像类一样,所以当声明委托变量后,如果不赋值则该委托变量为null。所以在使用委托变量前最好做非空校验if(weituo!=null) 四、泛型委托Action与Action<T>
、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:如果自己写非泛型委托:需要保存一个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();
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>。
、Action<T>委托(泛型版本)
Action<T>委托的泛型版本是一个无返回值,但是参数个数及类型可以改变的委托。
例子3:
class Program
{
static void Main(string[] args)
{
Action<string> action1 = M1;
action1("内置无返回值的泛型委托");
Action<int> action2 = M1;
action2();
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委托只有泛型版本的,接受参数个数可以是若干个,也可以是没有参数,但是一定有返回值的方法(下节讲)
课程6 委托、匿名方法、Lambda表达式、LINQ查询表达式
上课日志2
一、补充—VS中自定义C#快速简写代码(输入简写字母后按两次Tab键自动输入)
在VS中选择工具——>代码段管理器(语言下拉框选择Visual C#,如下图1所示),位置下面的下拉列表框再选择Visual C#,然后复制位置文本框里的路径,即找到代码段简写文件(*.snippet)。
然后随便复制一个(比如cw.snippet),复制的位置可以任意(注意两点:第一、不要在原来的cw.snippet位置,第二、需要新建一个文件夹(zdy)来存储复制过来的cw.snippet文件)
然后再对复制过来的文件修改(比如需要创建快速输入Console.ReadKey(),可以把文件名改为crk.snippet),接着打开重命名后的文件(crk.snippet)修改里面的内容(如图2所示),参照图3进行修改(修改绿色框住的4个地方即可)
最后单击图1下面的添加按钮,选择到自定义的文件夹(zdy)。
图1
图2
图3
二、泛型委托Func<T>
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;
}
}
三、多播委托
多播委托就是一个委托同时绑定多个方法,多播委托也叫委托链、委托组合。
1、绑定无返回值的多个委托
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);
}
}
2、绑定有返回值的多个委托,如何获取到每个方法的返回值
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";
}
}
四、匿名方法
1、匿名类
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
}
2、匿名方法
匿名方法,即是没有名字的方法,不能直接在类中定义,而是在给委托变量赋值的时候,需要赋值一个方法,此时可以“现做现卖”,定义一个匿名方法传递给该委托。
匿名方法关键字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表达式
1、Lambda介绍
“Lambda 表达式”(lambda expression)就是一个匿名函数(匿名方法),Lambda表达式基于数学中的λ演算得名。
Lambda运算符:所有的lambda表达式都是用新的lambda运算符 " => ",可以叫他:“转到”或者 “成为”,读作 “goes to”。运算符将表达式分为两部分,左边指定输入参数,右边是lambda的主体(方法体)。
lambda表达式:
一个参数:param=>expr
多个参数:(param-list)=>expr
2、输入参数与表达式或语句块
输入参数:在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
六、匿名方法、Lambda表达式应用巩固举例
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简介
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子句:连接多个用于查询操作的数据源。
八、Form子句
创建一个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);
}
}
}
九、where子句、select子句
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
十、Group子句、into子句及orderby子句
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"可以省略。因为默认排序方式为升序。
十一、let子句、join子句
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;
课程6 委托、匿名方法、Lambda表达式、LINQ查询表达式
上课日志2
一、补充—VS中自定义C#快速简写代码(输入简写字母后按两次Tab键自动输入)
在VS中选择工具——>代码段管理器(语言下拉框选择Visual C#,如下图1所示),位置下面的下拉列表框再选择Visual C#,然后复制位置文本框里的路径,即找到代码段简写文件(*.snippet)。
然后随便复制一个(比如cw.snippet),复制的位置可以任意(注意两点:第一、不要在原来的cw.snippet位置,第二、需要新建一个文件夹(zdy)来存储复制过来的cw.snippet文件)
然后再对复制过来的文件修改(比如需要创建快速输入Console.ReadKey(),可以把文件名改为crk.snippet),接着打开重命名后的文件(crk.snippet)修改里面的内容(如图2所示),参照图3进行修改(修改绿色框住的4个地方即可)
最后单击图1下面的添加按钮,选择到自定义的文件夹(zdy)。
图1
图2
图3
二、泛型委托Func<T>
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;
}
}
三、多播委托
多播委托就是一个委托同时绑定多个方法,多播委托也叫委托链、委托组合。
1、绑定无返回值的多个委托
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);
}
}
2、绑定有返回值的多个委托,如何获取到每个方法的返回值
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";
}
}
四、匿名方法
1、匿名类
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
}
2、匿名方法
匿名方法,即是没有名字的方法,不能直接在类中定义,而是在给委托变量赋值的时候,需要赋值一个方法,此时可以“现做现卖”,定义一个匿名方法传递给该委托。
匿名方法关键字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表达式
1、Lambda介绍
“Lambda 表达式”(lambda expression)就是一个匿名函数(匿名方法),Lambda表达式基于数学中的λ演算得名。
Lambda运算符:所有的lambda表达式都是用新的lambda运算符 " => ",可以叫他:“转到”或者 “成为”,读作 “goes to”。运算符将表达式分为两部分,左边指定输入参数,右边是lambda的主体(方法体)。
lambda表达式:
一个参数:param=>expr
多个参数:(param-list)=>expr
2、输入参数与表达式或语句块
输入参数:在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
六、匿名方法、Lambda表达式应用巩固举例
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简介
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子句:连接多个用于查询操作的数据源。
八、Form子句
创建一个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);
}
}
}
九、where子句、select子句
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
十、Group子句、into子句及orderby子句
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"可以省略。因为默认排序方式为升序。
十一、let子句、join子句
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;