17xie > C# 2005 & .NET 3.0高级编程(第5版) > 9.7 Framework的其他泛型类型
背景:                 
[本书目录] [图书首页] [本书讨论区]  
链接地址:http://www.17xie.com/read-58534.html    注册17xie 一起来写书 实现您的出书梦想!

9.7  Framework的其他泛型类型

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

本节讨论如下内容:

结构Nullable<T>

委托EventHandler<TEventArgs>

结构ArraySegment<T>

9.7.1  结构Nullable<T>

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

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

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

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

Nullable<int> x;

x = 4;

x += 3;

if (x.HasValue)

{

   int y = x.Value;

}

x = null;

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

Nullable<int> x1;

int? x2;

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

int? x = GetNullableType(); Please use brackets here. Good coding standards. [CN]

can do it here

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;

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

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<TEventArgs>

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

public sealed delegate void EventHandler<TEventArgs>(object sender, TEventArgs e)

   where TEventArgs : EventArgs

9.7.3  ArraySegment<T>

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

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

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

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

ArraySegment<int> segment = new ArraySegment<int>(arr, 2, 3);

for (int i = segment.Offset; i < segment.Offset + segment.Count+1; i++)

{

   Console.WriteLine(segment.Array[i]);

}

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

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

void WorkWithSegment(ArraySegment<string> segment)

{

   for (int i = segment.Offset; i < segment.Offset + segment.Count; i++)

   {

      Console.WriteLine(segment.Array[i]);

   }

}

注意:

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


字数:3634    最后更新:7个月以前 [04-10 21:33]happyskynet 修改
本页编辑者:happyskynet  
[前一页]:9.6 泛型委托  [后一页]:9.8 小结
[在本页中加入书签] [收藏本书] [推荐本书]
  17xie论坛 > 本书讨论区 > 本页评论   (共0条)
发表评论

用户名称 匿名发表
评论内容
验证码

关于我们 | 版权声明 | 免责声明 | 诚聘英才 | 联系我们 | 合作伙伴 | 友情链接 | 广告合作 | 提交意见
Copyright © 2007 17xie.com 互联网协同写书平台 京ICP备08002671号