可空类型(译)

原文:http://www.spring4d.org/spring4d-in-action/nullable-types/


 

08年,Allen Bauer 写了 “Nullable” . 这个有趣的东西没有被纳入 Delphi RTL,但它在Spring4D有一段时间了。

那么,什么是nullable类型,它又应该在什么时候使用呢?让我们从下面的一个示例得到答案。

// uses Spring;
TPerson = class
private
fBirthDate: Nullable<TDateTime>;
public
property BirthDate: Nullable<TDateTime> read fBirthDate write fBirthDate;
property Age: Nullable<Integer> read GetAge; // Calculated Property
end;



一个人的出生日期属于选择性输入值,因此,可能无法计算他的年龄。然而Nullable<T>类型完美的适应了这种情形。否则我们必须蹩脚的使用某些特别的值(magic number)来表示它的空状态。它更容易将数据库的的数据类型映射到Delphi中,因为大多数数据类型均可为空。

现在让我们展示一个最简形式的Nullable<T>:

Nullable<T> = packed record
private
fValue: T;
fHasValue: string;
public
constructor Create(const value: T); overload;
constructor Create(const value: Variant); overload;

function GetValueOrDefault: T; overload;
function GetValueOrDefault(const defaultValue: T): T; overload;

function Equals(const other: Nullable<T>): Boolean;

property HasValue: Boolean read GetHasValue;
property Value: T read GetValue;
end;



Nullable<T>是一个记录类型封装器,它不需要预先初始化就能够被调用。T是可空类型的基本类型,T可以是任何值类型,如Integer, TDateTime, Boolean, String,甚至另一个记录类型。下面的例子演示了如何使用一个可为空值的整形:

procedure TestNullableUsage;
var
age: Nullable<Integer>;
value: Integer;
begin
Assert(not age.HasValue);

// An EInvalidOperationException exception will be raised
// when accessing the Value property of a null variable
try
age.Value;
except on e: Exception do
Assert(e is EInvalidOperationException);
end;

Assert(age.GetValueOrDefault = Default(Integer));
Assert(age.GetValueOrDefault(30) = 30);

// 在 Nullable<T> 和 T 间隐式转换。
age := 30;
value := age;

// Equality Operators
Assert(age = value);
Assert(age <> 20);

// Assign nil to the nullable value to make it null
age := nil;
Assert(not age.HasValue);
end;



核心提示:

1.Nullable<T>被设计为不可修改的。
Nullable<T>本身不提供任何改变内部状态和值的方法。如果你需要可写属性,你必须声明一个setter,就象TPerson.BirthDate一样,否则属性是只读的。

2.和Variant的兼容性
当你传递一个类型为varNull或varEmpty的variant值给一个可空变量时,它将被标记为空。

3.相等性操作

function Nullable<T>.Equals(const other: Nullable<T>): Boolean;
begin
if HasValue and other.HasValue then
Result := TEqualityComparer<T>.Default.Equals(Value, other.Value)
else
Result := HasValue = other.HasValue;
end;



4.巧妙的实现HasValue
既然我们不能为结构类型定义默认构造函数,那么在值没有被初始化的情况下,如何确保HasValue属性总是返回false呢?在记录类型中用一个boolean标志可不是个好办法。

procedure TestNullableHasValue;
var
age: Nullable<Integer>; // Lives in stack
begin
Assert(not age.HasValue);
end;


Hallvard Vassbotn在那个贴子的回复中提醒了我们,当记录中包含一个托管类型字段(如string, interface)时,编译器会自动初始化一次。我们在Spring4D中使用字符串标志。

constructor Nullable<T>.Create(const value: T);
begin
fValue := value;
fHasValue := CHasValueFlag; // '@'
end;

function Nullable<T>.GetHasValue: Boolean;
begin
Result := fHasValue <> '';
end;





本文是“实用中的Spring4D”系列中的一部分。

http://www.spring4d.org

Copyright 2012 (c) Baoquan Zuo

转载于:https://www.cnblogs.com/zzzl/archive/2012/03/21/2410825.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值