5 隐式类型数组
隐式类型数组定义语法为:
Ø 数组创建表达式:
new [] 数组初始化器
隐式类型数组实例的类型是根据数组初始化器推导出来的,所以这个数组初始化器只能包含确切的一种类型(也可以包含可以隐式转换成这种类型的类型),而且这个类型不是null,否则将产生一个编译错误。
var a = new[] { 1, 10, 100, 1000 }; // int[]
var b = new[] { 1, 1.5, 2, 2.5 }; // double[]
var c = new[] { "hello", null, "world"}; // string[]
var d = new[] { 1, "one", 2, "two" }; // 编译错误,int和string不能进行隐式转换,这种情况必须显示的指定数组类型,如object[]
隐式类型数组可以和匿名类型一块使用来创建匿名类型的数据结构:如:
var contacts = new[] { new {Name = "Chris Smith",PhoneNumbers = new[]{ "206-555-0101", "425-882-8080" }},
new {Name = "Bob Harris",PhoneNumbers = new[] { "650-555-0199" }}};
6 扩展方法 扩展方法是可以使用实例名来调用的静态方法,是通过增加新的功能方法到已存在的类型中去的一种途径。扩展方法必须是声明在静态类中的静态方法,并且扩展方法的第一个参数必须带一个this关键字,然后就可以通过第一个参数类型的实例进行调用,也就是可以象实例方法一样调用。例如:
namespace LazyBee.Utilities
{
public static class Extensions
{
public static int ToInt32(this string s)
{ return Int32.Parse(s);}
public static T[] Slice<T>(this T[] source, int index, int count)
{
if (index < 0 || count < 0 || source.Length-index < count) throw new ArgumentException();
T[] result = new T[count];
Array.Copy(source, index, result, 0, count);
return result;
}
}
}
然后在你要调用扩展方法的地方导入LazyBee.Utilities命名空间,就可以象如下方式调用:
string s = "1234";
int i = s.ToInt32(); // Same as Extensions.ToInt32(s)
int[] digits = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
int[] e = digits.Slice(4, 3); // Same as Extensions.Slice(digits, 4, 3)
注意:
1 实例方法优先于扩展方法的调用,也就是说只有不存在符合要求的实例方法,才会去查找时候存在扩展方法。
2 扩展方法具有一般静态方法所拥有的一切特性。
3 扩展方法比实例方法具有更多的限制,以及更低的优先级。所以要尽量少用扩展方法,建议只有在不能增加实例方法的情况下使用。
4 其他类型的扩展成员如属性、事件、操作目前不支持,不过微软正在考虑当中。
7 Lambda表达式 6.1 Lambda表达式提供一种更精简的方式来写匿名方法,其语法格式为:
Ø 表达式:
赋值表达式
非赋值表达式
Ø 非赋值表达式:
条件表达式
Lambda表达式
查询表达式
Ø Lambda表达式:
(Lambda参数列表可选)=>Lambda表达式体
隐式类型Lambda参数=> Lambda表达式体
Ø Lambda参数列表:
显式类型Lambda参数列表
隐式类型Lambda参数列表
Ø 显式类型Lambda参数列表:
显式类型Lambda参数
显式类型Lambda参数列表,显式类型Lambda参数
Ø 显式类型Lambda参数:
参数修饰符可选 类型 标识符
Ø 隐式类型Lambda参数列表
隐式类型Lambda参数
隐式类型Lambda参数列表,隐式类型Lambda参数
Ø 隐式类型Lambda参数:
标识符
Ø Lambda表达式体:
表达式
语句块
示例:
x => x + 1; // Implicitly typed, expression body
x => { return x + 1; }; // Implicitly typed, statement body
(int x) => x + 1; // Explicitly typed, expression body
(int x) => { return x + 1; }; // Explicitly typed, statement body
() => Console.WriteLine("OK"); //无参数列表
(x,y) => x*y; // Multiple parameters
注意:1 如果是隐式类型,并且只有一个参数时,可以省略括号,如(x) => x + 1,可以简写成x => x + 1
2 隐式类型和显式类型不能混合使用
6.2 当代理类型和Lambda表达式满足下列条件时,我们说它们是兼容的,也就是可以将Lambda表达式赋值给该代理类型:
1 代理类型和Lambda表达式具有相通数量的参数;
2 如果Lambda表达式是显式类型参数列表,那么每一个参数的类型要和对应的代理类型的参数的类型和修饰符相同;如果是隐式类型参数列表,那么代理类型中不能有ref和out的参数,根据Lambda表达式体推导出的参数类型要和代理类型的参数一致。
3 如果Lambda表达式体是一个语句块,并且代理类型的返回值是void,那么语句块中不能包含有包含返回值的return语句;如果代理类型的返回值不是void,那么语句块必须是包含返回值的return语句,而且返回值的类型要和代理类型的返回值一致。
4 如果Lambda表达式体是一个表达式,并且代理类型的返回值是void,那么表达式必须是能作为语句的表达式(只能是赋值表达式,对对象或函数的调用,new 表达式,变量的++,--操作表达式之一);如果代理类型的返回值不是void,那么根据表达式推导出的类型必须能和代理类型的返回值类型一致。
示例:
public delegate R Func<T,R>(T t);
public delegate void F2();
public delegate R F3<T,R>(T t,R r);
Func<int, int> a = x => x + 1;
F2 b = () => Console.WriteLine("OK");
F3<int, int> c = (x,y) => x*y;
下例将会出现编译错误:
public delegate void F3<T,R>(T t,R r);
F3<int, int> c = (x,y) => x*y;//Compile error: Only assignment, call, increment, decrement, and new object expressions can be used as a statement
6.3 当Lambda表达式传递给一个没有指定类型参数的泛型方法时,.Net架构将根据表达式来推导出相应的类型参数,例如:
public static class Extensions
{
public static IEnumerable<S> MySelect<T, S>(this IEnumerable<T> source,Func<T, S> selector)
{ foreach (T element in source) yield return selector(element); }
}
var contacts = new[] { new {Name = "Chris Smith",PhoneNumbers = new[]{ "206-555-0101", "425-882-8080" }},
new {Name = "Bob Harris",PhoneNumbers = new[] { "650-555-0199" }}};
IEnumerable<string> name = contacts.MySelect(j => j.Name);
在这里MySelect是一个扩展函数,所以contacts.MySelect(j => j.Name)运行时将翻译成Extensions.MySelect(contacts,j => j.Name),在这里T就是包含Name和电话号码的匿名类型,所以S就是string 类型。
6.4 如果存在两个相同名称的带有Lambda表达式为参数的方法,在隐式转换过程中输入参数和返回值的类型以最容易转换为原则进行匹配,如:
class ItemList<T> : List<T>
{
public int Sum<T>(Func<T, int> selector)
{
int sum = 0;
foreach (T item in this) sum += selector(item);
return sum;
}
public double Sum<T>(Func<T, double> selector)
{
double sum = 0;
foreach (T item in this) sum += selector(item);
return sum;
}
}
class Detail
{
public int UnitCount;
public double UnitPrice;
}
void ComputeSums()
{
ItemList<Detail> orderDetails = GetOrderDetails();
int totalUnits = orderDetails.Sum(d => d.UnitCount);
double orderTotal = orderDetails.Sum(d => d.UnitPrice * d.UnitCount);
}