泛型结构
与类相似,结构也可以是泛型的。它们非常类似于泛型类,只是没有继承特性。
.NET Framework中 的一个泛型结构是 Nullable<T>。数据库中的数字和编程语言中的数字有显著不同的特征,因为数据库中的数字可以为空,而 C# 中的数字不能为空。Int32 是一个结构,而结构的实现同值类型,所以结构不能为空。这个问题不仅存在于在数据库中,也存在于把 XML 数据映射到,NET类型。
这种区别常常令人很头痛,映射数据也要多做许多辅助工作。一种解决方案是把数据库和XML文件中的数字映射为引用类型,因为引用类型可以为空值。但这也会在运行期间带来额外的系统开销。
使用 Nullable<T> 结构很容易解决这个问题。下面的代码段说明了如何定义 Nullable<T> 的一个简化版本。
结构 Nullable<T> 定义了一个约束:其中的泛型类型 T 必须是一个结构。把类定义为泛型类型后,就没有低系统开销这个优点了,而且因为类的对象可以为空,所以对类使用 Nullable<T> 型是没有意义的。除了 Nullable<T> 定义的T类型之外,唯一的系统开销是 hasValue 布尔字段,它确定是设置对应的值,还是使之为空。除此之外,泛型结构还定义了只读属性 HasValue 和 value,以及一些操作符重载。 把 Nullable<T> 类型强制转换为T类型的操作符重载是显式定义的,因为当 hasValue为false时,它会抛出一个异常。强制转换为 Nullable<T> 类型的操作符重载定义为隐式的,因为它总是能成功地转换:
public class Nullable<T> where T:struct { private bool hasValue; public bool HasValue { get { return hasValue; } set { hasValue = value; } } private T value; public T Value { get { if (!hasValue) { throw new InvalidOperationException("no value"); } return value; } } public Nullable(T value) { this.hasValue = true; this.value = value; } public static explicit operator T(Nullable<T> value) { return value.Value; } public static implicit operator Nullable<T>(T value) { return new Nullable<T>(value); } public override string ToString() { if (!HasValue) return String.Empty; return this.value.ToString(); } }
在这个例子中,Nullable<T> 用 Nullable<int> 实例化。变量x现在可以用作一个 int,进行赋值或使用运算符执行一些计算。这是因为强制转换了Nullable<T> 类型的运算符。但是,x还可以为空。Nullable<T>的 HasValue 和 Value 属性可以检查是否有一个值,该值是否可以访问:
static void Main(string[] args) { Nullable<int> x; x = 4; x += 3; if (x.HasValue) { int y = x.Value; } x = null; }