全國最多中醫師線上諮詢網站-台灣中醫網
發文 回覆 瀏覽次數:3776
推到 Plurk!
推到 Facebook!

Assign 和 AssignTo 的深度研究

 
maomfh
初階會員


發表:3
回覆:10
積分:27
註冊:2008-01-05

發送簡訊給我
#1 引用回覆 回覆 發表時間:2008-08-07 12:48:13 IP:218.163.xxx.xxx 訂閱
書名:Delphi Component Design
作者:Danny Thorpe

摘錄書中的一篇文章 Assign And AssignTo 分享給大家. 文中我翻譯的不好,還請大家見諒.


Assign and AssignTo (賦值 和 賦值到)
Assignment of data to component properties is one of the most fundamentaloperations in Delphi.
Since component properties can refer to object instances and object instances with properties can be
treated like record structures with data fields, you need a way to allow the Delphi programmer to assign
one class-type component property the "value" of another class-type property.
However,just making the simple instance variable assignment A := B won't do: You'll lose
the object instance that variable A was referring to, and you'll wind up with two
variables referring to the same object instance.
This is a problem if both A and B assume they own the object instance they refer to-if A destroys the object
instance, B will point to garbage.
在Delphi中對元件的屬性賦值是最基本的操作之一.
由於元件的屬性可以參考到物件,而物件的屬性就像是Record結構中的欄位,你需要有一個方法
讓Delphi的程式員能指派一個元件的屬性值到另一個元件的屬性值上.
但無論如何,只是一個簡單的物件賦值A:=B卻無法做到內容複製:這樣做你將會丟失物件A所參考的物件,而且你
最終將有二個變數參考到一個相同物件.
如果A和B都認為他擁有所參考到的物件,這會是一個問題,假如A解構了這個物件,B將會指向一堆垃圾.
To make A := B perform as expected, all you need to do is make A take on the attributes of B.
If you can do this without destroying either object instance, you avoid problems of object ownership and
ownership transfer.
VCL implements an object attribute transfer convention using the TPersistent.Assign method and its less
common reciprocal, TPersistent.AssignTo.
Where C has copy constructors, implicitly created temporary instances, and assignment operators, Delphi has
Assign methods and property write methods.
要使得 A:=B這個操作如預期般的行為, 你所要做的是讓A具有B的屬性.
假如你可以不需要解構任一個物件完成這個,那你也就避免物件擁有權的問題和擁有權移轉的問題.
VCL實作一個物件屬性傳送的規範,那就是使用 TPersisten.Assign這個方法,和較不常見的替換方法
TPersistent.AssignTo. C 有拷貝建構子,隱含著建立暫時性物件,和賦值操作子,Delphi有Assign方法和屬
性寫入的方法.

Making the Assignment (賦值操作)
First, let's talk about how to set up a class-type property to support direct assignment.
Let's say we're implementing a string list property of type TStrings.
In the property declaration, specify a method in the property's write clause, as shown in Listing 8.1.
For this discussion of assignment, it doesn't matter whether the property read clause is a method or a field.
首先,我們來說說如何建立一個 '物件型屬性' 能支援直接賦值操作.
我們這麼說,我們可以實作一個TStrings型別的字串串列屬性.
在屬性宣告中,屬性的write語句中指定一個方法,像列表8.1這樣.
對於賦值的討論,我們不管屬性的read語句中是一個方法或欄位都沒有關係.

Listing 8.1 The SetList method lets the MyStrings property support Orect assignment.
type
TMyComponent =class(TComponent)
private
FList TStringList;
procedure SetList (NewList TStrings);
public
constructor Create (OWner:TComponent); Override;
destructor Destroy; override;
property MyStrings : TStrings read FList write SetList;
end;
constructor TMyComponent.Create (Owner: TComponent);
begin
inherited Create(Owner);
FList := TStringList.Create;
end;
destructor TMyComponent.Destroy;
begin
FList.Free;
inherited Destroy;
end;
procedure TMyComponent.SetList(NewList: TStrings);
begin
FList.Assign(NewList);
end;
Implement the SetList method to call the Assign method of the internal stringlist instance, passing in the
NewList parameter.
The TStrings implementation of Assign takes care of the details of copying the list contents from the NewList
instance into the current string list instance, essentially cloning the list.
實作SetList這個方法,只是呼叫內部StringList物件的Assign方法即可,傳送NewList這個參數.
TStrings完成Assign,它處理從NewList物件複制到目前字串串列的串列內容的細節,基本上就是複制串列.

Implementing Assign (實作Assign)
When building your own component class, you need to think about whether you should support assignment
between instances of your class and what it means to make such assignments.
The destination instance should usually release all resources it has allocated and copy or acquire a shared
reference to the resources owned by the source instance.
In the simplest case, you just copy property values from the source instance (the parameter to Assign) to the
destination instance (Self in Assign).
In extreme cases, it may be simpler to dump the source instance into a temporary memory stream and load the
destination instance from that memory stream.
當建立你自己的元件類別時,你需要想一下,你是否要支援物件間的賦值操作,且這樣的賦值是什麼意義.
目的物件通常應該釋出他所配置的全部資源,然後複製或取得來源物件的共享參照的資源.
最簡單的例子,你只要從來源物件複制屬性值(Assign的參數)到目的物件(Assign的本身物件).
在最極端的例子,他可能很簡單的將來源物件倒入一個暫時性記憶體中,然後目的物件再從記憶體中載入.

Most VCL components don't implement any support for assignment between instances.
Most VCL helper classes - TPersistent descendants intended for use as properties in components, such as TFont, TPen,
and TBrush - do support assignment.
A few special classes, such as TClipboard, contain extensive assignment support.
大部份的VCL元件不實作任何支援物件間的賦值操作.
大部份VCL輔助類別 - TPersistent的子類預期被使用於元件中當屬性的,像是TFont, TPen, 和TBrush,就有支援賦
值操作了.一些特殊的類別,像是TClipboard, 則包含了延伸的賦值支援.
You should consider what class types your component will recognize as com-
patible sources. You could support assignment of instances of other component
types to your component if it makes sense and is convenient for the Delphi appli-
cation writer. Most classes recognize only their own class type in their Assign
implementations.
你應該考量有那些類別,你的元件能識為相容的來源.
假如你可以識別出其他元件的話,你就可以支援從其他類別物件賦值到本身,
那對Delphi的應用程式設計員來說就很方便了.
大部份的類別在Assign實作中僅會識別自身類別.
Your Assign implementation should be prepared for nil as a source parame-
ter. If it makes sense for your component, assignment of a nil source parameter
should revert your component to its default initialized state. If your component
cannot support nil assignments, treat nil like any other source parameter you
don't support: Pass it on to the inherited Assign method.
你的Assign方法的實作應該對nil這樣的來源參數有所準備.
假如能識別出nil,你應該回復你的元件到他預設的初始狀態.
假如你的元件無法支援nil的賦值,那就把nil當你不支援的來源參數:把nil傳給你的父代Assign方法去處理.
To implement Assign, you override TPersistent.Assign in your TPersistent-
derived class declaration and write code to test the source parameter for nil or
compatible class type(s). Source instance class types you don't recognize or han-
dle completely should be passed on to the inherited Assign method. Listing 8.2
shows the implementation of TStrings.Assign, which clears the destination string
list and copies each of the strings from the source list to the destination list.
要實作Assign, 你可在TPesistent的子類中宣告覆蓋TPersistent.Assign,並寫碼來測試來源參數是否為nil或
相容型別.來源物件類別的型別你無法識別或無法完全處理的,那就應傳給父代的Assign方法來處理.
列表8.2展示TStrings.Assign的實作,它清除自身的字串串列而且複制來源物件的字串串列到自身的字串串列上.
Listing 8.2 A simple implementation of Assign
procedure TStrings.Assign (Source: TPersistent);
begin
if Source is TStrings then
begin
BeginUpdate;
try
Clear;
AddStrings(TStrings(Source));
finally
EndUpdate;
end;
end else inherited Assign(Source);
end;
Since TStrings is an abstract base class used by many components for string
data properties, this Assign implementation makes it easy to assign
Listboxl.Lines := Memol.Lines or Headerl.Sections := StringGrid.Rows[10]. In
each case, the property type is a class descended from TStrings, and the property
write methods use Assign to transfer the data between the string lists.
由於TStrings是一個抽象基礎別,被很多元件用來當作字串資料屬性,這個Assign的實作讓
Listbox1.Lines := Memo1.Line 或 Headerl.Sections := StringGrid.Rows[10] 的賦值操作變得很容易.
兩個分別都是TStrings的子類別的屬性,且屬性的寫入方法使用Assign來傳送字串串列間的資料.

The AssignTo Turnabout (AssignTo的思想轉換)
When your Assign method calls its inherited method, your ancestor class has
an opportunity to examine the source instance's class type and either handle the
assignment or pass the source instance on to its inherited method. If none of the
Assign implementations in your class's ancestry know what to do with the source
instance, execution will eventually find its way to TPersistent.Assign. If the
source parameter is nil, TPersistent raises an exception; otherwise, TPersis-
tent.Assign turns around and calls the AssignTo method of the source instance,
passing the destination instance as a parameter. The process of implementing
AssignTo is identical to Assign, except that the roles are reversed (the parameter
is the destination, and Self is the source).
當你的Assign方法呼叫它繼承的方法時,你的父代類別就有機會來檢驗來源物件的類別型別,
這樣就可以,不管是要自己處理或可再傳給自己的父類別來處理.假如所有的父類別的Assign都
不知道如何處理來源物件,這個處理最後會丟給TPersistent.Assign來處理.
假如來源參數是nil,TPersistent會發出一個例外;否則,TPersistent.Assign將來個迴轉,轉而呼叫來源物件的
AssignTo方法,傳入自身當呼叫參數.AssignTo這個實作的過程和Assign相同,除了角色相反之外.(參數是
目的,而本身是來源)
The AssignTo turnabout says, "If A doesn't know how to extract data from B,
perhaps B knows how to transfer data into A." This allows new classes to participate in assignment to and from
old classes without modifying the code of the old classes.
In broader terms, the combination of Assign and AssignTo allows a class
to implement assignment semantics for assignments to instances of that class
from known class types, as well as assignments from instances of that class to
other known class types.
AssignTo的思想轉換可以這麼說,"假如A不知如何從B抽取資料,也許B知道如何傳送資料到A."
這樣允許新的類別參加賦值操作,而不需修改舊類別的程式碼. 廣的說,Assign和AssignTo的結合,
允許一個類別實作賦值這個語意,從已知類別賦值到物件本身上,而且從物件本身賦值給自己已知的類別.
Non-Self Class Tests and Smart Linking (非自身類別測試和智慧聯結)
Be careful of what class types you touch when implementing your Assign
and AssignTo methods. Remember that an IS test touches the class information of
the named class type, which links in all the virtual methods of the class and all
the code they refer to. Testing an instance against your own class type or an
inherited class type incurs no code size penalties (you're already using your
class), but testing an instance against some other class type usually forces the
linker to link in that other class, even if it's never used or constructed anywhere
in the application. Since Assign and AssignTo are virtual methods, sloppy coding
can lead to a multilevel cascade effect where use of class A in a program touches
class B (because A.Assign contains an IS B test) and class B touches classes C and
D (because B.Assign contains IS C and IS D tests), and so on, which is a classic
cause of code bloat.
當實作你自己的Assign和AssignTo的方法時,小心你碰觸的是什麼樣的類別.
記住當你用Is測試你碰觸的類別時,這樣會連結進該類別的VMT及參考到的程式碼都連結進來.
測試一個物件參考到自身類別或繼承的類別不會招致程式碼膨脹的損失.(你已使用了你自身的類別了),
但是測試一個物件對照到其他類別,通常會強制連結器連入其他的類別,甚至這個類別從未用於程式中或未曾
於程式中建構任何物件.由於Assign和AssignTo是虛擬方法,粗心的程式碼可能導致多層串接效應,在程式中使用
類別A碰觸類別B(因為A.Assign包含一個 IS B 的測試),而類別B又碰觸類別C和D(因為B.Assign包含 IS C和 IS D的測
試), 等等... 這是一個典型造成程式碼膨脹.
The best strategy to combat this code bloat is to eliminate codependencies in
Assign and AssignTo. One way to avoid touching specific types is to use base
class types instead of multiple specific descendant class types. If you can get the
info you need using C and D's common ancestor type instead of the C and D
actual types, you reduce the code bloat costs to just the implementation of the
common ancestor class instead of the implementations of both C and D.
對抗程式碼膨脹最好的策略是在Assign和AssignTo中排除相依的程式碼.
一個方法來避免碰觸特定的型別是使用基礎型別來替代多重的子類別.
假如你可以使用類別C和D的共同父代類別來替代使用C和D類別取得你需要的資訊.
你可以降低程式碼的膨脹只是類別C和D的共用父類別而已,而不是兩個類別C和D.
You can sacrifice one class to carry the burden of cross-class assignment for
several classes. If one class already requires the presence of the other classes, put
all the Assign/AssignTo logic in that class. In the preceding example, if there is
no way to avoid B's use of C and D, you can at least lighten A by flipping A.Assign's code into B.AssignTo.
Instead of A.Assign containing an IS B test,
B.AssignTo can have an IS A test. B gets a little heavier, but A gets a lot lighter.
A real-world example of this is the TPicture class. TPicture can contain a bitmap, a metafile, an icon, or a custom graphic image,
so using a TPicture class in a project will most likely pull those other graphics classes into the EXE as well.
You want to be able to assign image data from a TPicture into a graphics class, like Bit-
map.Assign(Picture), for example. Problem: You don't want the graphics class's
Assign methods to reference TPicture because TPicture touches so many other
things. Solution: Make TPicture.AssignTo do the work of transferring its image
into an arbitrary graphics class.
你可以為一些類別犧牲一個類別來攜有穿越類別間賦值的重責大任.
假如某個類別已經需要另外的類別,那就在該類別置入Assign和AssignTo的邏輯.
在前面的例子中,假如沒有方法避免類別B使用類別C和D,你至少藉由反轉A.Assign的程式碼變成B.AssignTo
來使得A輕盈些. B.AssignTo可以有一個 IS A類別的測試,來替代A.Assign中包含IS B類別的測試, 類別B會笨重些,
但類別A變輕盈許多.
一個真實的例子是TPicture類別. TPicture可以包含Bitmap, Metafile, Icon,或客製影像, 所以在專案中使用TPicture類別,
同樣也將幾乎扯入那些其他繒圖類別到EXE檔中.
你想要能指派影像資料從TPicture到另一個繒圖類別.例如,像是Bitmap.Assign(Picture). 問題是:你不想要graphics類別
的Assign方法參考到TPicture,因為TPicture碰觸到太多其他的東西了. 解答是: 使TPicture.AssignTo來完成這工作,傳
送TPicture的自身影像到當時的graphics類別中.
Frequency of use plays into this as well. The cost of a frequently used class
touching a seldom used class is very high; the seldom used class is dragged in
even though it is not used. The cost of a seldom-used class touching a frequently-
used class is quite low; the frequently used class is probably already in the project
for other reasons. For example, the clipboard unit provides special support for
handling various clipboard data formats. This support is handy but is not needed
by most Delphi applications. Instead of having the graphics classes support
assignment to the clipboard component (and thus drag infrequently used clip-
board code into all Delphi apps), the clipboard component supports assignment
from the graphics classes in its AssignTo method. The clipboard component is the
seldom used class, while the graphics classes are frequently used classes.
一個經常使用的類別碰觸一個罕用的類別的代價是很大的;罕用類別甚至沒有使用也會被扯入程式中.
一個罕用類別碰觸常用類別的代價非常的小;常用類別可能因其他理由已經存在專案中了.
例如,clipboard單元提供特別的支援來處理各樣的clipboard資料格式.這個支援很方便,但對大部份的Delphi應用程式
並不是必須的.取而代之的是讓graphics類別支援賦值到clipboard元件中,(因此扯入不常用的clipboard程式碼
進入Delphi應用程式中),由於graphics類別的AssignTo方法,clipboard元件支援賦值操作.
clipboard元件是罕用類別,同時graphics類別是常用類別.
Transferring Data Via Properties or Drect Field Access (傳送資料經由屬性或直接欄位存取)
When implementing Assign or AssignTo, it may be necessary to circumvent
the normal property assignment practices of your component to avoid triggering
multiple change notifications in your component or to prevent the data transfer
operation from changing the state of the source instance. Since most Assign
implementations deal only with source instances compatible with their own class
type. Assign usually has complete access to the private fields at the heart of both
the destination and source instances.
當實作Assign或AssignTo時,很可能需要規避元件正常屬性賦值的操作習慣,來避免在元件中觸發多重的改變訊息
通知,或防止因為改變來源物件狀態而發生的資料傳送操作.
由於大部份的Assign實作都僅處理與自身相容的來源物件. Assign通常有完全的存取權,來存取目的和來源物件的
核心私有欄位.
For example, one simple way to implement TBitmap.Assign would be to just
assign the source's bitmap handle and palette handle into the destination's bit-
map and palette properties.
However, referring to the source's Handle property
will force it to create a bitmap handle if it doesn't already have one (VCL's handle
creation deferral strategies are covered in Chapter 10).
Creating a handle is a time-consuming task, and grabbing the handle marks the bitmap as modified
(since VCL can't know what you're going to do with the raw handle, it must assume you're going to draw onto it).
Instead of referring to the source's Handle property, TBitmap.Assign reaches into the private fields of the source TBitmap so
that the destination instance can share the bitmap data owned by the source instance without changing the source instance's
state.
Like all the VCL graphics classes, TBitmap implements copy-on-write resource caching. Modifications to one bitmap instance
whose image data is shared with other bitmap instances will force it to make its own complete copy of the image data before
applying the modifications.
例如,一個簡單的方法來實作TBitmap.Assign只要賦值來源的bitmap Handle和palette Handle到目的物件的bitmap
和palette中.無論如何,假如它本身沒有一個hanle的話,參照到來源物件的Handle屬性將迫使它建立一個bitmap
handle (VCL的handle延期建立策略被涵蓋在第10章).
建立handle是一個費時的工作,並且抓住handle標記bitmap已經修改過.
(由於VCL不知道,你將要用這個原始handle做什麼,它必須假定你將會在上面畫圖).
TBitmap.Assign深入到來源物件TBitmap的私有欄位,替代參考到來源物件的handle屬性,所以目的物件可以共享
來源物件的bitmap資料而不需要改變來源物件的狀態.
就像全部的VCL graphics類別,TBitmap實作copy-on-write(寫入時拷貝)的資源快取. 修改一個已分享於其他bitmap物件
的影像資料,在申請修改之前,將強迫它自己建立它自己擁有影像的拷貝.

Clipboard Support (剪貼板的支援)
The best example of Assign and AssignTo in action is the TClipboard class and
its interaction with components. TClipboard includes built-in support for transfer-
ring clipboard data to and from the standard graphics classes - for example, TBit-
map and TPicture. Through TPicture, TClipboard also supports any custom
graphics classes that have been registered through TPicture.RegisterClipboardFor-
mat. To copy data to the clipboard, you call Clipboard.Assign(MyObject). To paste
data from the clipboard into your object, you call MyObect.Assign(Clipboard).
Assign和AssignTo的最好的例子是TClipboard類別及和它互動的元件.
TClipboard包含內建的支援傳送clipboard資料到其他物件及從標準graphics物件賦值給自身.例如,TBitmap和TPicture.
經由TPicture,TClipboard同樣也支援任何的客製graphics類別,只要已經由TPicture.RegisterClipboardFormat註冊即可.
要複製資料到clipboard,你可呼叫Clipboard.Assign(MyObject). 要貼入clipboard的資料到你的物件內,你可呼叫
MyObject.Assign(Clipboard)即可.
If you create a new component that has special clipboard data requirements,
you may be tempted to create a new clipboard class type derived from TClip-
board, override the Assign method, and add your special support code directly to
the clipboard. Don't do this If you replace the stock clipboard object with your
own custom clipboard instance, you'll have to destroy the stock clipboard and
create your replacement in the unit initialization code (since the clipboard object
must always be available). Now, what happens if somebody else does the same
thing to implement his or her own custom clipboard support?
If these two custom clipboards wind up in the same project (they will, trust me), one of the custom clipboard implementations
will end up destroying the other, and the code that relies on the existence of the losing custom clipboard will most likely cause
an access violation.
Again, don't do this! The clipboard is a global resource; there
can be only one, so leave it alone.
假如你建立一個新元件,它有特別的clipboard資料的要求,你也許很想要建立一個新的clipboard類別,讓新類別繼
承自TCilpboard,然後覆寫Assign,且直接在新的clipboard加入你的特殊支援程式碼.
不要這麼做,假如你用你自己客製的clipboard物件覆蓋系統的clipboard物件,在單元的initialization段,你將必須解構
系統的clipboard物件而且建立你的所替換的那個物件(由於clipboard物件必須永遠有效).現在,假如有其他人也做
相同的事他也客製自己的clipboard物件,那會發生什麼? 假如有兩個客製clipboard必須存在於相同的專案中(很有
可能,想信我), 其中一個客製clipboard實作將會結束且解構另一個, 而依賴於這個已消失的客製clipboard的程式碼
將很可能造成一個非法存取.再一次,不要這麼做! clipboard是一個全域的資源;只能有一個,所以離它遠一點.
To get the custom clipboard support you need, just implement your special
clipboard support code in your component's Assign (paste from clipboard) and
AssignTo (copy to clipboard) methods, and remind your customers (the applica-
tion writers) to use Assign to perform clipboard operations on your component.
要能支援客製的clipboard,只要在你的元件的Assign(從clipboard貼入) 和AssignTo(複製到clipboard)
實作你的特殊clipboard支援碼.而且提醒你的客戶(應用程式編寫者)在你的元件中使用Assign去完成
clipboard的操作.
------
Maomfh
careychen
尊榮會員


發表:41
回覆:580
積分:959
註冊:2004-03-03

發送簡訊給我
#2 引用回覆 回覆 發表時間:2008-08-07 13:53:04 IP:218.210.xxx.xxx 訂閱
嗯,不錯哦,給你拍拍手,謝謝你的文章

這篇有一個是 陳寬達 先生免費提供的資料,他的書中第 68 頁也是有講到這個哦,可以參考看看

Delphi 深度歷險
------
價值的展現,來自於你用哪一個角度來看待它!!
系統時間:2024-03-29 5:24:55
聯絡我們 | Delphi K.Top討論版
本站聲明
1. 本論壇為無營利行為之開放平台,所有文章都是由網友自行張貼,如牽涉到法律糾紛一切與本站無關。
2. 假如網友發表之內容涉及侵權,而損及您的利益,請立即通知版主刪除。
3. 請勿批評中華民國元首及政府或批評各政黨,是藍是綠本站無權干涉,但這裡不是政治性論壇!