贝壳电子书 > 电脑IT电子书 > c#高级编程(第6版)--第九章 >

第4章

c#高级编程(第6版)--第九章-第4章

小说: c#高级编程(第6版)--第九章 字数: 每页4000字

按键盘上方向键 ← 或 → 可快速上下翻页,按键盘上的 Enter 键可回到本书目录页,按键盘上方向键 ↑ 可回到本页顶部!
————未阅读完?加入书签已便下次继续阅读!




TrueForAll()


bool Predicate(T match)


TrueForAll()方法给每个元素调用谓词委托。如果谓词给每个元素都返回true,则TrueForAll()也返回true。如果谓词为一个元素返回了false,TrueForAll()就返回false

 

下面看看如何使用这些方法。

Sort()方法把这个委托作为参数:

public delegate int parison(T x; T y);

这样,就可以使用l表达式传送两个Person对象,给数组排序。对于Person对象数组,参数T是Person类型:

Person'' persons = {

new Person(〃Emerson〃; 〃Fittipaldi〃);

new Person(〃Niki〃; 〃Lauda〃);

new Person(〃Ayrton〃; 〃Senna〃);

new Person(〃Michael〃; 〃Schumacher〃)

};

Array。Sort(persons; (p1; p2)=》 p1。FirstnamepareTo(p2。Firstname));

Array。ForEach()方法将Action委托作为参数,给数组的每个元素执行操作:

public delegate void Action(T obj);

于是,就可以传送Console。WriteLine方法的地址,将每个人写入控制台。WriteLine()方法的一个重载版本将Object类作为参数类型。由于Person派生于Object,所以它适合于Person数组:

Array。ForEach(persons; Console。WriteLine);

ForEach()语句的结果将persons变量引用的集合中的每个人都写入控制台:

Emerson Fittipaldi

Niki Lauda

Ayrton Senna

Michael Schumacher

如果需要更多的控制,则可以传送一个l表达式,其参数应匹配委托定义的参数:

Array。ForEach(persons; p=》 Console。WriteLine(〃{0}〃; p。Lastname));

下面是写入控制台的姓氏:

Fittipaldi

Lauda

Senna

Schumacher

Array。FindAll()方法需要Predicate委托:

public delegate bool Predicate(T match);

Array。FindAll()方法为数组中的每个元素调用谓词,并返回一个谓词是true的数组。在这个例子中,对于Lastname以字符串〃S〃开头的所有Person对象,都返回true。

Person'' sPersons = Array。FindAll(persons; p=》 p。Lastname。StartsWith(〃S〃);

迭代返回的集合sPersons,并写入控制台,结果如下:

Ayrton Senna

Michael Schumacher

ArraynvertAll()方法使用泛型委托Converter和两个泛型类型。第一个泛型类型TInput是输入参数,第二个泛型类型TOutput是返回类型。

public delegate TOutput Converter(TInput input);

如果一种类型的数组应转换为另一种类型的数组,就可以使用ConvertAll()方法。下面是一个与Person类无关的Racer类。Person类有Firstname和Lastname属性,而Racer类为赛手的姓名定义了一个属性Name:

public class Racer



public Racer(string name)



this。name = name;

} 

public string Name {get; set}

public string Team {get; set}



使用ArraynvertAll(),很容易将persons数组转换为Racer数组。给每个Person元素调用委托。在每个Person元素的匿名方法的执行代码中,创建了一个新的Racer对象,将firstname和lastname连接起来传送给带一个字符串参数的构造函数。结果是一个Racer对象数组:

Racer'' racers =

ArraynvertAll(

persons; p = 》

new Racer(String。Format(〃{0} {1}〃; p。FirstName; p。LastName));

9。7  Framework的其他泛型类型

除了Systemllections。Generic命名空间之外, Framework还有其他泛型类型。这里讨论的结构和委托都位于System命名空间中,用于不同的目的。

本节讨论如下内容:

●       结构Nullable

●       委托EventHandler

●       结构ArraySegment
9。7。1  结构Nullable

数据库中的数字和编程语言中的数字有显著不同的特征,因为数据库中的数字可以为空,C#中的数字不能为空。Int32是一个结构,而结构实现为值类型,所以它不能为空。

只有在数据库中,而且把XML数据映射为类型,才不存在这个问题。

这种区别常常令人很头痛,映射数据也要多做许多工作。一种解决方案是把数据库和XML文件中的数字映射为引用类型,因为引用类型可以为空值。但这也会在运行期间带来额外的系统开销。

使用Nullable结构很容易解决这个问题。在下面的例子中,Nullable用Nullable实例化。变量x现在可以像int那样使用了,进行赋值或使用运算符执行一些计算。这是因为我们转换了Nullable类型的运算符。x还可以是空。可以检查Nullable的HasValue和Value属性,如果该属性有一个值,就可以访问该值:

Nullable x;

x = 4;

x += 3;

if (x。HasValue)



   int y = x。Value;



x = null;

因为可空类型使用得非常频繁,所以C#有一种特殊的语法,用于定义这种类型的变量。定义这类变量时,不使用一般结构的语法,而使用?运算符。在下面的例子中,x1和x2都是可空int类型的实例:

Nullable x1;

int? x2;

可空类型可以与null和数字比较,如上所示。这里,x的值与null比较,如果x不是null,就与小于0的值比较:

int? x = GetNullableType();

if (x  null)



Console。WriteLine(〃x is null〃);



else if (x 《 0)



Console。WriteLine(〃x is smaller than 0〃);



可空类型还可以使用算术运算符。变量x3是变量x1和x2的和。如果这两个可空变量中有一个的值是null,它们的和就是null。

int? x1 = GetNullableType();

int? x2 = GetNullableType();

int? x3 = x1 + x2;

提示:

这里调用的GetNullableType()方法只是任意返回可空int的方法的占位符。为了进行测试,可以把它实现为只返回null或返回任意整数值。

非可空类型可以转换为可空类型。从非可空类型转换为可空类型时,在不需要强制类型转换的地方可以进行隐式转换。这种转换总是成功的:

int y1 = 4;

int? x1 = y1;

但从可空类型转换为非可空类型可能会失败。如果可空类型的值是null,把null值赋予非可空类型,就会抛出InvalidOperationException类型的异常。这就是进行显式转换时需要类型转换运算符的原因:

int? x1 = GetNullableType();

int y1 = (int)x1;

如果不进行显式类型转换,还可以使用接合运算符(coalescing operator)从可空类型转换为非可空类型。接合运算符的语法是??,为转换定义了一个默认值,以防可空类型的值是null。这里,如果x1是null,y1的值就是0。

int? x1 = GetNullableType();

int y1 = x1 ?? 0;
9。7。2  EventHandler

在Windows Forms和Web应用程序中,为许多不同的事件处理程序定义了委托。其中一些事件处理程序如下:

public sealed delegate void EventHandler(object sender; EventArgs e);

public sealed delegate void PaintEventHandler(object sender; PaintEventArgs e);

public sealed delegate void MouseEventHandler(object sender; MouseEventArgs e);

这些委托的共同点是,第一个参数总是sender,它是事件的起源,第二个参数是包含事件特定信息的类型。

使用新的EventHandler,就不需要为每个事件处理程序定义新委托了。可以看出,第一个参数的定义方式与以前一样,但第二个参数是一个泛型类型TeventArgs。where子句指定TEventArgs的类型必须派生于基类EventArgs。

public sealed delegate void EventHandler(object sender; TEventArgs e)

   where TEventArgs : EventArgs
9。7。3  ArraySegment

结构ArraySegment表示数组的一段。如果需要数组的一部分,就可以使用数组段。在ArraySegment中,包含了数组段的信息(偏移量和元素个数)。

在下面的例子中,变量arr定义为有8个元素的int数组。ArraySegment类型的变量segment用于表示该整数数组的一段。该段用构造函数初始化,在这个构造函数中,传送了该数组、偏移量和元素个数。其中偏移量设置为2,所以从第三个元素开始,元素个数设置为3,所以6是数组段的最后一个元素。

数组段可以用Array属性访问。ArraySegment还有Offset和Count属性,表示定义数组段的初始化了的值。for循环用于迭代数组段。for循环的第一个表达式初始化为迭代开始的偏移量。第二个表达式指定数组段中的元素个数,以确定迭代是否停止。在for循环中,数组段包含的元素用Array属性来访问:

int'' arr = {1; 2; 3; 4; 5; 6; 7; 8};

ArraySegment segment = new ArraySegment(arr; 2; 3);

for (int i = segment。Offset; i 《 segment。Offset + segmentunt; i++)



   Console。WriteLine(segment。Array'i');



在上面的例子中,ArraySegment结构有什么用处?ArraySegment可以作为参数传送给方法。这样,只要一个参数就可以定义数组、偏移量和元素个数,而不是3个参数。

WorkWithSegment()方法把ArraySegment作为参数。在这个方法的实现代码中,Offset、Count和Array属性的用法与以前相同:

void WorkWithSegment(ArraySegment segment)



   for (int i = segment。Offset; i 《 segment。Offset + segmentunt; i++)

   {

      Console。WriteLine(segment。Array'i');

   }



注意:

数组段不复制原数组的元素,但原数组可以通过ArraySegment访问。如果数组段中的元素改变了,这些变化也会反映到原数组中。

9。8  小结

本章介绍了 2。0中一个非常重要的特性:泛型。通过泛型类可以创建独立于类型的类,泛型方法是独立于类型的方法。接口、结构和委托也可以用泛型的方式创建。泛型引入了一种新的编程方式。我们介绍了算法(尤其是操作和谓词)如何用于不同的类,而且它们都是类型安全的。泛型委托可以去除集合中的算法。

 Framework的其他类型包括Nullable、EventHandler和ArraySegment。

下一章利用泛型来介绍集合类。


返回目录 上一页 回到顶部 0 0

你可能喜欢的