|
f (Bitmap.Width = 0) or (Bitmap.Height = 0) then Exit; Y := 0; while Y < ClientHeight do begin X := 0; while X < ClientWidth do begin Canvas.Draw(X, Y, Bitmap); Inc(X, Bitmap.Width); end; Inc(Y, Bitmap.Height); end; end; // Set a new bitmap by copying the TBitmap object. procedure TTile.SetBitmap(NewBitmap: TBitmap); begin fBitmap.Assign(NewBitmap); end; end.
Interfaces
An interface defines a type that comprises abstract virtual methods. Although a class inherits from a single base class, it can implement any number of interfaces. An interface is similar to an abstract class (that is, a class that has no fields and all of whose methods are abstract), but Delphi has extra magic to help you work with interfaces. Delphi''''s interfaces sometimes look like COM (Component Object Model) interfaces, but you don''''t need to know COM to use Delphi interfaces, and you can use interfaces for many other purposes.
You can declare a new interface by inheriting from an existing interface. An interface declaration contains method and property declarations, but no fields. Just as all classes inherit from TObject, all interfaces inherit from IUnknown. The IUnknown interface declares three methods: _AddRef, _Release, and QueryInterface. If you are familiar with COM, you will recognize these methods. The first two methods manage reference counting for the lifetime of the object that implements the interface. The third method accesses other interfaces an object might implement.
When you declare a class that implements one or more interfaces, you must provide an implementation of all the methods declared in all the interfaces. The class can implement an interface''''s methods, or it can delegate the implementation to a property, whose value is an interface. The simplest way to implement the _AddRef, _Release, and QueryInterface methods is to inherit them from TInterfacedObject or one of its derived classes, but you are free to inherit from any other class if you wish to define the methods yourself.
A class implements each of an interface''''s methods by declaring a method with the same name, arguments, and calling convention. Delphi automatically matches the class''''s methods with the interface''''s methods. If you want to use a different method name, you can redirect an interface method to a method with a different name. The redirected method must have the same arguments and calling convention as the interface method. This feature is especially important when a class implements multiple interfaces with identical method names. See the class keyword in Chapter 5 for more information about redirecting methods.
A class can delegate the implementation of an interface to a property that uses the implements directive. The property''''s value must be the interface that the class wants to implement. When the object is cast to that interface type, Delphi automatically fetches the property''''s value and returns that interface. See the implements directive in Chapter 5 for details.
For each non-delegated interface, the compiler creates a hidden field to store a pointer to the interface''''s VMT. The interface field or fields follow immediately after the object''''s hidden VMT field. Just as an object reference is really a pointer to the object''''s hidden VMT field, an interface reference is a pointer to the interface''''s hidden VMT field. Delphi automatically initializes the hidden fields when the object is constructed. See Chapter 3 to learn how the compiler uses RTTI to keep track of the VMT and the hidden field.
Reference counting
The compiler generates calls to _AddRef and _Release to manage the lifetime of interfaced objects. To use Delphi''''s automatic reference counting, declare a variable with an interface type. When you assign an interface reference to an interface variable, Delphi automatically calls _AddRef. When the variable goes out of scope, Delphi automatically calls _Release.
The behavior of _AddRef and _Release is entirely up to you. If you inherit from TInterfacedObject, these methods implement reference counting. The _AddRef method increments the reference count, and _Release decrements it. When the reference count goes to zero, _Release frees the object. If you inherit from a different class, you can define these methods to do anything you want. You should implement QueryInterface correctly, though, because Delphi relies on it to implement the as operator.
Typecasting
Delphi calls QueryInterface as part of its implementation of the as operator for interfaces. You can use the as operator to cast an interface to any other interface type. Delphi calls QueryInterface to obtain the new interface reference. If QueryInterface returns an error, the as operator raises a runtime error. (The SysUtils unit maps the runtime error to an EIntfCastError exception.)
You can implement QueryInterface any way you want, but you probably want to use the same approach taken by TInterfacedObject. Example 2-13 shows a class that implements QueryInterface normally, but uses stubs for _AddRef and _Release. Later in this section, you''''ll see how useful this class can be.
Example 2-13: Interface Base Class Without Reference Counting type
TNoRefCount = class(TObject, IUnknown)
protected
function QueryInterface(const IID:TGUID; out Obj):HResult; stdcall;
function _AddRef: Integer; stdcall;
function _Release: Integer; stdcall;
end;
function TNoRefCount.QueryInterface(const IID:TGUID; out Obj): HResult;
begin
if GetInterface(IID, Obj) then
Result := 0
else
Result := Windows.E_NoInterface;
end;
function TNoRefCount._AddRef: Integer;
begin
Result := -1
end;
function TNoRefCount._Release: Integer;
begin
Result := -1
end;
Interfaces and object-oriented programming
The most important use of interfaces is to separate type inheritance from class inheritance. Class inheritance is an effective tool for code reuse. A derived class easily inherits the fields, methods, and properties of a base class, and thereby avoids reimplementing common methods. In a strongly typed language, such as Delphi, the compiler treats a class as a type, and therefore class inheritance becomes synonymous with type inheritance. In the best of all possible worlds, though, types and classes are entirely separate.
Textbooks on object-oriented programming often describe an inheritance relationship as an "is-a" relationship, for example, a TSavingsAccount "is-a" TAccount. You can see the same idea in Delphi''''s is operator, where you test whether an Account variable is TSavingsAccount.
Outside of textbook examples, though, simple is-a relationships break down. A square is a rectangle, but that doesn''''t mean you want to derive TSquare from TRectangle. A rectangle is a polygon, but you probably don''''t want to derive TRectangle from TPolygon. Class inheritance forces a derived class to store all the fields that are declared in the base class, but in this case, the derived class doesn''''t need that information. A TSquare object can get away with storing a single length for all of its sides. A TRectangle object, however, must store two lengths. A TPolygon object needs to store many sides and vertices.
The solution is to separate the type inheritance (a square is a rectangle is a polygon) from class inheritance (class C inherits the fields and methods of class B, which inherits the fields and methods of class A). Use interfaces for type inheritance, so you can leave class inheritance to do what it does best: inheriting fields and methods.
In other words, ISquare inherits from IRectangle, which inherits from IPolygon. The interfaces follow the "is-a" relationship. Entirely separate from the interfaces, the class TSquare implements ISquare, IRectangle, and IPolygon. TRectangle implements IRectangle and IPolygon.
TIP: The convention in COM programming is to name interfaces with an initial I. Delphi follows this convention for all interfaces. Note that it is a useful convention, but not a language requirement.
On the implementation side, you can declare additional classes to implement code reuse. For example, TBaseShape implements the common methods and fields for all shapes. TRectangle inherits from TBaseShape and implements the methods in a way that make sense for rectangles. TPolygon also inherits from TBaseShape and implements the methods in a way that make sense for other kinds of polygons.
A drawing program can use the shapes by manipulating IPolygon interfaces. Example 2-14 shows simplified classes and interfaces for this scheme. Notice how each interface has a GUID (Globally Unique Identifier) in its declaration. The GUID is necessary for using QueryInterface. If you need the GUID of an interface (in an explicit call to QueryInterface, for example), you can use the interface name. Delphi automatically converts an interface name to its GUID.
Example 2-14: Separating Type and Class Hierarchies type
IShape = interface
[''''{50F6D851-F4EB-11D2-88AC-00104BCAC44B}'''']
procedure Draw(Canvas: TCanvas);
function GetPosition: TPoint;
procedure SetPosition(Value: TPoint);
property Position: TPoint read GetPosition write SetPosition;
end;
IPolygon = interface(IShape)
[''''{50F6D852-F4EB-11D2-88AC-00104BCAC44B}'''']
function NumVertices: Integer;
function NumSides: Integer;
上一页 [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)
|