分享
 
 
 

C# 3.0语言规范

王朝c#·作者佚名  2006-11-24
窄屏简体版  字體: |||超大  

C# 3.0(C# Orcas——魔鬼)在C# 2.0的基础上引入了很多语言扩展,用以支持高级别的函数式风格类库的创建和使用。这些扩展使得结构性API构造具有与其他领域(如关系数据库和XML)中查询语言同等的表达能力。这些扩展包括:

具有隐式类型的局部变量,允许通过用于初始化的表达式来推断局部变量的类型。

扩展方法,使得对一个现存类型的扩展和构造具有附加方法的类型变为现实。

拉姆达(Lambda)表达式,匿名方法的一种进化,为委托类型和表达式树提供了改进的类型推断和转换。

对象初始化器,使得构造和初始化对象变得容易。

匿名类型,由对象初始化器推断和创建出来的类型。

具有隐式类型的数组,从数组初始化器推断出元素类型并进行创建和初始化的数组。

查询表达式,提供了集成的查询语法,与关系、分级查询语言如SQL和XQuery类似。

表达式树,允许将拉姆达表达式表现为数据(表达式树),而不是代码(委托)。

1 具有隐式类型的局部变量

在一个具有隐式类型的局部变量声明(implicitly typed local variable declaration)中,被声明的局部变量的类型是通过初始化该变量的表达式推断出来的。当使用局部变量声指示符符var来代替类型,并且当前作用域内没有名为var的类型时,这个声明便成为一个具有隐式类型的局部变量声明。例如:

var i = 5;

var s = "Hello";

var d = 1.0;

var numbers = new int[] {1, 2, 3};

var orders = new Dictionary<int, Order>();

上面这些具有隐式类型的局部变量声明和下面这些具有显式类型的声明完全一致:

int i = 5;

string s = "Hello";

double d = 1.0;

int[] numbers = new int[] {1, 2, 3};

Dictionary<int, Order> orders = new Dictionary<int, Order>();

一个具有隐式类型的局部变量声明中的局部变量声明器(Declarator)必须遵循下列约束:

该声明器必须包含初始化器。

初始化器必须是一个表达式。该初始化器不能是它自己的对象或集合初始化器(第4部分),但可以是一个包含了对象或集合初始化器的new表达式。

初始化器表达式在编译期的类型必须不能为空类型。

如果局部变量的声明包含多个声明器,所有的初始化器在编译期都必须具有相同的类型。

下面是不正确的具有隐式类型的局部变量声明示例:

var x; // 错误,没有用来推断类型的初始化器

var y = {1, 2, 3}; // 错误,不允许使用集合初始化器

var z = null; // 错误,不允许出现空类型

出于向下兼容的原因,当一个局部变量声明指示符以var作为类型,但当前作用域中有一个名为var的类型时,这个声明使用的是该类型;然而,(编译器)会针对这种模糊的语义给出一个警告。不过由于var违反了类型名字首字母必须大写这条约定,这种情况应该不大会出现。

for语句的for-initializer和using语句的resource-acquisition可以是一个具有隐式类型的局部变量声明。同样,foreach语句中的迭代变量也可以被声明为具有隐式类型的局部变量,在这种情况下,迭代变量的类型通过待遍历的集合的元素类型来推断。

int[] numbers = {1, 3, 5, 7, 9};

foreach(var n in numbers) Console.WriteLine(n);

在上面的例子中n的类型被推断为int——numbers的元素类型。

2 扩展方法

扩展方法(Extension Method)是一种静态方法,可以通过实例方法的语法进行调用。从最终效果上看,扩展方法使得扩展一个现有类型和构造一个具有附加方法的类型变成了现实。

注意

扩展方法很难发觉,并且比起实例方法在功能性上有很大限制。出于这些原因,我们建议保守地使用扩展方法,仅在实例方法不大可行或根本不可行的时候才使用。

扩展成员的其他类型,如属性、事件和运算符都在考虑之中,但目前并未支持。

2.1 声明扩展方法

扩展方法通过在方法的第一个参数上指定关键字this作为一个修饰符来声明。扩展方法只能声明在静态类中。下面的示例是一个声明了两个扩展方法的静态类:

namespace Acme.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 ArugmentException();

T[] result = new T[count];

Array.Copy(source, index, result, 0, count);

return result;

}

}

}

扩展方法和正常的静态方法具有完全相同的功能。另外,一旦导入了扩展方法,就可以用调用实例方法的语法来调用扩展方法。

2.2 导入扩展方法

扩展方法使用using-namespace-directives导入。除了导入一个命名空间中的类型以外,一个using-namespace-directive还可以导入一个命名空间中所有的静态类中所有的扩展方法。最后,导入的扩展方法表现为其第一个参数的类型的附加方法,并且其优先级比一般的实例方法低。例如,当使用using-namespace-directive导入了上面例子中的Acme.Utilities命名空间时:

using Acme.Utilities;

就可以使用调用实例方法的语法来调用静态类Extensions中的扩展方法了:

string s = "1234";

int i = s.ToInt32(); // 和Extensions.ToInt32(s)一样

int[] digits = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};

int[] a = digits.Slice(4, 3); // 和Extensions.Slice(digits, 4, 3)一样

2.3 扩展方法的调用

下面描述了扩展方法调用的详细规则。在下面这些形式的方法调用中:

expr . identifier ( )

expr . identifier ( args )

expr . identifier < typeargs > ( )

expr . identifier < typeargs > ( args )

如果按照正常的过程没有发现可用的实例方法(确切地说,当待调用的候选方法集合为空时),就会尝试构造一个扩展方法调用。这些方法调用首先被重写为下面相应的形式:

identifier ( expr )

identifier ( expr , args )

identifier < typeargs > ( expr )

identifier < typeargs > ( expr , args )

然后将重写后的形式作为一个静态方法调用进行处理,identifier按照下列顺序进行解析:首先是命名空间生命中最接近的声明,然后是每一个接近的命名空间,最后是包含这些代码的编译单元,其间不断尝试重写过的方法调用,这些方法来自一个方法组,该组由using-namespace-directives导入的命名空间中所有可见的identifier所提供的可见的扩展方法构成。第一个产生了非空候选方法集合的方法组是对冲洗过的方法调用的一个选择。如果所有的尝试都产生了空的候选方法集合,就会出现一个编译期错误。

上述规则意味着实例方法的优先级胜于扩展方法,并且最后引入的命名空间中的扩展方法的优先级胜于较先引入的命名空间中的扩展方法。例如:

using N1;

namespace N1

{

public static class E

{

public static void F(this object obj, int i) { }

public static void F(this object obj, string s) { }

}

}

class A { }

class B

{

public void F(int i) { }

}

class C

{

public void F(object obj) { }

}

class X

{

static void Test(A a, B b, C c)

{

a.F(1); // E.F(object, int)

a.F("Hello"); // E.F(object, string)

b.F(1); // B.F(int)

b.F("Hello"); // E.F(object, string)

c.F(1); // C.F(object)

c.F("Hello"); // C.F(object)

}

}

在这个例子中,B的方法优先于第一个扩展方法,而C的方法优先于所有两个扩展方法。

3 拉姆达表达式

C# 2.0中引入了匿名方法,允许在期望出现委托的时候以“内联(in-line)”的代码替代之。尽管匿名方法提供了函数式编程语言中的很多表达能力,但匿名方法的语法实在是太罗嗦了,并且很不自然。拉姆达表达式(Lambda expression)为书写匿名方法提供了一种更加简单、更加函数化的语法。

拉姆达表达式的书写方式是一个参数列表后跟=>记号,然后跟一个表达式或一个语句块。

expression:

assignment

non-assignment-expression

non-assignment-expression:

conditional-expression

lambda-expression

query-expression

lambda-expression:

( lambda-parameter-listopt ) => lambda-expression-body

implicitly-typed-lambda-parameter => lambda-expression-body

lambda-parameter-list:

explicitly-typed-lambda-parameter-list

implicitly-typed-lambda-parameter-list

explicitly-typed-lambda-parameter-list:

explicitly-typed-lambda-parameter

explicitly-typed-lambda-parameter-list , explicitly-typed-lambda-parameter

explicitly-typed-lambda-parameter:

parameter-modifieropt type identifier

implicitly-typed-lambda-parameter-list:

implicitly-typed-lambda-parameter

implicitly-typed-lambda-parameter-list , implicitly-typed-lambda-parameter

implicitly-typed-lambda-parameter:

identifier

lambda-expression-body:

expression

block

拉姆达表达式的参数可以具有显式的或隐式的类型。在一个具有显式类型的参数列表中,每个参数的类型都是显式声明的。在一个具有隐式类型的参数列表中,参数的类型是从拉姆达表达式出现的上下文中推断出来的——具体来说,是当拉姆达表达式被转换为一个兼容的委托类型时,该委托类型提供了参数的类型。

当拉姆达表达式只有一个具有隐式类型的参数时,参数列表中的括号可以省略。换句话说,下面这种形式的拉姆达表达式:

( param ) => expr

可以简写为:

param => expr

下面给出的是拉姆达表达式的一些例子:

x => x + 1 // 隐式类型,以表达式作为拉姆

[1] [2] [3] [4] [5] 下一页

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有