|
''''s type cannot be a dynamic array. Records and arrays can be nested, and you can even use variant records. Example 2-9 shows an extended rectangle type, similar to the Windows TRect type, but because it is a class, it has properties and methods.Example 2-9: Properties Readers and Writers
TRectEx = class(TPersistent)
private
R: TRect;
function GetHeight: Integer;
function GetWidth: Integer;
procedure SetHeight(const Value: Integer);
procedure SetWidth(const Value: Integer);
public
constructor Create(const R: TRect); overload;
constructor Create(Left, Top, Right, Bottom: Integer); overload;
constructor Create(const TopLeft, BottomRight: TPoint); overload;
procedure Assign(Source: TPersistent); override;
procedure Inflate(X, Y: Integer);
procedure Intersect(const R: TRectEx);
function IsEmpty: Boolean;
function IsEqual(const R: TRectEx): Boolean;
procedure Offset(X, Y: Integer);
procedure Union(const R: TRectEx);
property TopLeft: TPoint read R.TopLeft write R.TopLeft;
property BottomRight: TPoint read R.BottomRight write R.BottomRight;
property Rect: TRect read R write R;
property Height: Integer read GetHeight write SetHeight;
property Width: Integer read GetWidth write SetWidth;
published
property Left: Integer read R.Left write R.Left default 0;
property Right: Integer read R.Right write R.Right default 0;
property Top: Integer read R.Top write R.Top default 0;
property Bottom: Integer read R.Bottom write R.Bottom default 0;
end;
Array properties
Properties come in scalar and array flavors. An array property cannot be published, but they have many other uses. The array index can be any type, and you can have multidimensional arrays, too. For array-type properties, you must use read and write methods--you cannot map an array-type property directly to an array-type field.
You can designate one array property as the default property. You can refer to the default property by using an object reference and an array subscript without mentioning the property name, as shown in Example 2-10.
Example 2-10: Using a Default Array Property type
TExample = class
...
property Items[I: Integer]: Integer read GetItem write SetItem;
property Chars[C: Char]: Char read GetChar write SetChar; default;
end;
var
Example: TExample;
I: Integer;
C: Char;
begin
Example := TExample.Create;
I := Example.Items[4]; // Must mention property name explicitly
C := Example[''''X'''']; // Array property is default
C := Example.Chars[''''X'''']; // Same as previous line
Indexed properties
You can map many properties to a single read or write method by specifying an index number for each property. The index value is passed to the read and write methods to differentiate one property from another.
You can even mix array indices and an index specifier. The reader and writer methods take the array indices as the first arguments, followed by the index specifier.
Default values
A property can also have stored and default directives. This information has no semantic meaning to the Delphi Pascal language, but Delphi''''s IDE uses this information when storing form descriptions. The value for the stored directive is a Boolean constant, a field of Boolean type, or a method that takes no arguments and returns a Boolean result. The value for the default directive is a constant value of the same type as the property. Only enumerated, integer, and set-type properties can have a default value. The stored and default directives have meaning only for published properties.
To distinguish a default array from a default value, the default array directive comes after the semicolon that ends the property declaration. The default value directive appears as part of the property declaration. See the default directive in Chapter 5 for details.
Using propertiesA common approach to writing Delphi classes is to make all fields private, and declare public properties to access the fields. Delphi imposes no performance penalty for properties that access fields directly. By using properties you get the added benefit of being able to change the implementation at a future date, say to add validation when a field''''s value changes. You can also use properties to enforce restricted access, such as using a read-only property to access a field whose value should not be changed. Example 2-11 shows some of the different ways to declare and use properties.
Example 2-11: Declaring and Using Properties type
TCustomer = record
Name: string;
TaxIDNumber: string[9];
end;
TAccount = class
private
fCustomer: TCustomer;
fBalance: Currency;
fNumber: Cardinal;
procedure SetBalance(NewBalance: Currency);
published
property Balance: Currency read fBalance write SetBalance;
property Number: Cardinal read fNumber; // Cannot change account #
property CustName: string read fCustomer.Name;
end;
TSavingsAccount = class(TAccount)
private
fInterestRate: Integer;
published
property InterestRate: Integer read fInterestRate
write fInterestRate default DefaultInterestRate;
end;
TLinkedAccount = class(TObject)
private
fAccounts: array[0..1] of TAccount;
function GetAccount(Index: Integer): TAccount;
public
// Two ways for properties to access an array: using an index
// or referring to an array element.
property Checking: TAccount index 0 read GetAccount;
property Savings: TAccount read fAccounts[1];
end;
TAccountList = class
private
fList: TList;
function GetAccount(Index: Integer): TAccount;
procedure SetAccount(Index: Integer; Account: TAccount);
function GetCount: Integer;
protected
property List: TList read fList;
public
property Count: Integer read GetCount;
property Accounts[Index: Integer]: TAccount read GetAccount
write SetAccount; default;
end;
procedure TAccount.SetBalance(NewBalance: Currency);
begin
if NewBalance < 0 then
raise EOverdrawnException.Create;
fBalance := NewBalance;
end;
function TLinkedAccount.GetAccount(Index: Integer): TAccount;
begin
Result := fAccounts[Index]
end;
function TAccountList.GetCount: Integer;
begin
Result := List.Count
end;
function TAccountList.GetAccount(Index: Integer): TAccount;
begin
Result := List[Index]
end;
procedure TAccountList.SetAccount(Index: Integer; Account: TAccount);
begin
fList[Index] := Account
end;
Class-type properties
Properties of class type need a little extra attention. The best way to work with class-type properties is to make sure the owner object manages the property object. In other words, don''''t save a reference to other objects, but keep a private copy of the property object. Use a write method to store an object by copying it. Delphi''''s IDE requires this behavior of published properties, and it makes sense for unpublished properties, too.
The only exception to the rule for class-type properties is when a property stores a reference to a component on a form. In that case, the property must store an object reference and not a copy of the component.
Delphi''''s IDE stores component references in a .dfm file by storing only the component name. When the .dfm is loaded, Delphi looks up the component name to restore the object reference. If you must store an entire component within another component, you must delegate all properties of the inner component.
Make sure the property''''s class inherits from TPersistent and that the class overrides the Assign method. Implement your property''''s write method to call Assign. (TPersistent--in the Classes unit--is not required, but it''''s the easiest way to copy an object. Otherwise, you need to duplicate the Assign method in whatever class you use.) The read method can provide direct access to the field. If the property object has an OnChange event, you might need to set that so your object is notified of any changes. Example 2-12 shows a typical pattern for using a class-type property. The example defines a graphical control that repeatedly displays a bitmap throughout its extent, tiling the bitmap as necessary. The Bitmap property stores a TBitmap object.
Example 2-12: Declaring and Using a Class-type Property
unit Tile; interface uses SysUtils, Classes, Controls, Graphics; type // Tile a bitmap TTile = class(TGraphicControl) private fBitmap: TBitmap; procedure SetBitmap(NewBitmap: TBitmap); procedure BitmapChanged(Sender: TObject); protected procedure Paint; override; public constructor Create(Owner: TComponent); override; destructor Destroy; override; published property Align; property Bitmap: TBitmap read fBitmap write SetBitmap; property OnClick; property OnDblClick; // Many other properties are useful, but were omitted to save space. // See TControl for a full list. end; implementation { TTile } // Create the bitmap when creating the control. constructor TTile.Create(Owner: TComponent); begin inherited; fBitmap := TBitmap.Create; fBitmap.OnChange := BitmapChanged; end; // Free the bitmap when destroying the control. destructor TTile.Destroy; begin FreeAndNil(fBitmap); inherited; end; // When the bitmap changes, redraw the control. procedure TTile.BitmapChanged(Sender: TObject); begin Invalidate; end; // Paint the control by tiling the bitmap. If there is no // bitmap, don''''t paint anything. procedure TTile.Paint; var X, Y: Integer; begin i 上一页 [1] [2] [3] [4] 下一页 [系统软件]InstallShield Express for delphi制作安装程序定… [系统软件]The GRETA Regular Expression Template Archive [系统软件]OLE with the internet explorer [系统软件]14.5.10.1 Object creation expressions [常用软件]InstallShield Express制作Delphi数据库安装程序 [常用软件]Firefox: What’s the next step? [VB.NET程序]VB.Net中文教程(8) 对象(Object)基本概念 [VB.NET程序]The UDPChat Source(VB.NET) [Delphi程序]为什么选择Delphi.Net ? [Delphi程序]《关于VisiBroker For Delphi的使用》(4)
|