rambo

面试语法糖(二)

一、C#中value

上下文关键字 value 用在普通属性声明的 set 访问器中。 此关键字类似于方法的输入参数。可以理解为和this指针,是系统自动提供的一个变量。

C#属性:它提供灵活的机制来读取、编写或计算某个私有字段的值。 可以像使用公共数据成员一样使用属性,但实际上它们是称作“访问器”的特殊方法。

TimePeriod 类存储一个时间段。 在内部,类以秒为单位存储时间,但Console Application使用名为 Hours 的属性能够以小时为单位指定时间。 Hours 属性的访问器执行小时和秒之间的转换。

二、yield return

在语句中使用 yield 关键字,则意味着它在其中出现的方法、运算符或 get 访问器是迭代器通过使用 yield 定义迭代器,可在实现自定义集合类型的 IEnumerableIEnumerator 模式时无需其他显式类。

IEnumerator <T>支持在泛型集合上进行简单迭代。

调用以下语句, 对迭代器函数的每个调用将继续到 yield return 语句的下一次执行。

通过 foreach 语句来使用迭代器方法。 foreach 循环的每次迭代都会调用迭代器方法。 //迭代器方法运行到 yield return 语句时,会返回一个 expression,并保留当前代码中的位置。 //当下次调用迭代器函数时执行从该位置重新启动。

foreach 语句体的每个迭代创建迭代器函数。迭代器方法和 get 访问器迭代器的声明必须满足以下要求:
1)返回类型必须为 IEnumerable、IEnumerable、IEnumerator 或 IEnumerator。
2)该声明不能有任何 ref 或out。

简单地说,当希望获取一个IEnumerable类型的集合,而不想把数据一次性加载到内存,就可以考虑使用yield return实现"按需供给"

yieldreturn

第一种方法,是把结果集全部加载到内存中再遍历;第二种方法,客户端每调用一次,yield return就返回一个值给客户端,是"按需供给"。

使用yield return为什么能保证每次循环遍历的时候从前一次停止的地方开始执行呢?

--因为,编译器会生成一个状态机来维护迭代器的状态

三、IDisposable模式

对于垃圾回收而言,在C#中,托管资源的垃圾回收是通过CLR的Garbage Collection来实现的,Garbage Collection会调用堆栈上对象的析构函数完成对象的释放工作;而对于一些非托管资源,比如数据库链接对象等,需要实现IDisposable接口进行手动的垃圾回收。

在生成Form的过程中,会有这样的代码

Dispose()方法允许抛出异常吗?答案是否定的。如果Dispose()方法有抛出异常的可能,那就需要使用try/catch来手动捕获。

四、C# Interface

接口只包含方法属性事件索引器的签名(但是不可以包含字段),属性和事件在编译的时候编译器会自动将其转化成方法。

实现接口的类(class)或结构(struct)必须实现接口定义中指定的接口成员。 在下面的示例,继承自类ISampleInterface的类Program 必须实现一个不具有参数并返回 void 的名为 SampleMethod 的方法。

//ISample obj = new ISample();//对象obj的列表中没有方法SampleMethod()。

//ISampleInterface obj = new ISample();

如果一个类实现了一个接口中的方法M,则M应该声明为public,否则编译无法通过。但是如何让类的引用无法使用接口中的方法M呢?

如何访问到SampleMethod()呢?通过接口类型的引用访问即可。

注意:接口是为不相关的类提供通用的功能的,像上面,如果SampleMethod()对类ISample的引用是隐藏的,完全可以将SampleMethod()这样的方法脱离出来再定义一个接口。所以照这样说,上面那种做法是没有必要的。

一个接口可从一个或多个基接口继承。

当基类型列表包含基类和接口时,基类必须是列表中的第一项。

实现接口的类可以显式实现该接口的成员。 显式实现的成员不能通过类实例访问,而只能通过接口实例访问!!!

Example:接口包含属性声明,类包含实现。实现IPoint的类的任何实例都具有整数属性x和y.

五、C# 接口和 抽象类(abstract class)

在Treeview中怎么将结点和Metadata联系起来呢?

1)抽象类是否可继承接口?
抽象类里面可以包括抽象方法和非抽象方法,而接口只是定义了方法的原型,没有方法的具体定义。从这方面来分析,抽象类是可以继承接口的。
2)接口可以继承抽象类么?
接口只定义成员方法的原型,并不包括成员方法的实现。抽象类里面可能有实现的方法。如果说,接口可以继承抽象类,那这就和“接口只声明成员方法的原型,而没有具体方法的定义”相违背。所以说,接口不能继承抽象类。
抽象类继承接口后,可以实现接口中的成员方法,也可以不实现。如果不实现接口中成员方法,只需将该方法声明为abstract类型就可以了。

3)(抽象)类继承类和接口的顺序是如何的?

基类A必须在任何接口之前,不然编译器会报错。

解释:针对上面的例子,假设类A中有一个方法M,接口IA中同时声明了一个方法M。如果类B是按照IA, IB, A这样的顺序继承,则在B类型执行内存分配时,B对象的方法表里面首先加载接口IA中的方法的实现,之后是IB的,再之后是A的。因为A中也有方法M,所以这样的顺序是不是会认为M是IA中成员M的实现呢?如果类B是按照A, IA, IB这样的顺序继承,就不存在这样的情况了。

4)接口中不能再声明类型。例如接口中不能再定义类,不能再声明接口等等,但是类里面可以再定义类,再声明接口。

五、abstract、override
abstract:
修饰类名为抽象类,修饰方法为抽象方法。如果一个类为抽象类,则这个类智能是其他某个类的基类。抽象方法在抽象类中没有函数体。抽象类中的抽象方法是没有方法体的,继承其的子类必须实现抽象类的抽象方法。

抽象类有如下特征:
抽象类不能实例化
抽象类的派生类必须实现所有抽象方法
抽象类中的抽象方法是没有方法体的,继承其的子类必须实现抽象类的抽象方法

抽象方法:
抽象方法是隐式的虚方法
只允许在抽象类中使用抽象方法声明
抽象方法在抽象类中没有方法体
在抽象方法声明中,不能使用static或者virtual修饰符

override:
override关键字提供派生类对基类方法的新的实现,重写的基类方法必须和基类的方法有着相同的签名(函数名、返回值、参数列表相同)。
override关键字不可以重写基类非virtual修饰的方法和static修饰的静态方法。
派生类的override方法和基类的virtual方法必须有相同的访问权限。不能用修饰符new、static、virtual或者abstract修饰override方法。
派生类的override方法重写的基类方法必须是virtual、abstract或者override的。

virtual:
virtual 关键字允许在派生类中重写这些对象。默认情况下,方法是非虚拟的,不可以重写非虚方法,virtual关键字不可以与static、abstract、private、override一起使用。virtual关键字又是和override紧密不可分的,如果要实现virtual方法就必须要使用override或new关键字(上文已经指出new和override产生的机理不同)。

sealed:

当对一个类应用sealed修饰符时,此修饰符会阻止其他类从该类继承。
sealed 方法必须与override连用,也就是说实现sealed方法的类的父类必须实现了此方法。
sealed关键字有两个作用:
1) 密封类不能被继承。
2 )密封方法重写基类中的方法,但其本身不能在任何派生类中进一步重写