2011-03-03 209 views
2

在德爾福我想確定一個特定的OleVariant是否可以轉換爲特定的數據類型而不會引發異常如果它不能。例外情況不適用於程序流程,對嗎?測試如果投擲OleVariant會引發異常(沒有引發異常)

我想是這樣的,其中Type可以由OleVariant支持什麼:

if TryVarAsType(variant, value) then ... 

要的是

try 
    value := Type(variant); 
    // case where the variant could be converted to a Type 
except 
    // case where the variant could not be converted to a Type 
end; 

的情況下變種無法轉換爲布爾值只是一個正常情況下經常發生,並不表示任何類型的錯誤。

+2

異常不是錯誤。例外是一個例外。這是不是規則,這是例外。另一條路。可能佔用堆棧多於一層的路徑。需要你編寫try..catch的路徑。在應用程序內故意引發和例行處理異常並不總是「錯誤狀態」,不應總是避免。有時,程序流程例外,特別是對於「無效輸入」條件。 – 2011-03-03 19:43:37

+0

授予異常是一個例外。但是就這個問題而言,任何代碼路徑都不例外,並且不能將變體轉換爲給定類型,這與條件一樣有效。 – 2011-03-04 08:31:02

回答

3

可以使用VariantChangeTypeEx功能構建這樣的功能。

uses 
    VarUtils, 
    Variants; 

function TryVarAsType(AVariant : OleVariant; const AVarType: TVarType) :Boolean; 
var 
    SourceType: TVarType; 
begin 
    SourceType:=TVarData(AVariant).VType; 
    //the types are ole compatible 
    if (AVarType and varTypeMask < varInt64) and (SourceType and varTypeMask < varInt64) then 
    Result:= 
    (SourceType=AVarType) or 
    (VariantChangeTypeEx(TVarData(AVariant), TVarData(AVariant), VAR_LOCALE_USER_DEFAULT, 0, AVarType)=VAR_OK) 
    else 
    Result:=False; //Here you must process the variant pascal types like varString 
end; 

和使用這樣

TryVarAsType('1',varInteger); 
TryVarAsType('s',varInteger) 

這將與只與OLE兼容的變量類型

varEmpty = $0000; { vt_empty  0 } 
    varNull  = $0001; { vt_null   1 } 
    varSmallint = $0002; { vt_i2   2 } 
    varInteger = $0003; { vt_i4   3 } 
    varSingle = $0004; { vt_r4   4 } 
    varDouble = $0005; { vt_r8   5 } 
    varCurrency = $0006; { vt_cy   6 } 
    varDate  = $0007; { vt_date   7 } 
    varOleStr = $0008; { vt_bstr   8 } 
    varDispatch = $0009; { vt_dispatch  9 } 
    varError = $000A; { vt_error  10 } 
    varBoolean = $000B; { vt_bool  11 } 
    varVariant = $000C; { vt_variant  12 } 
    varUnknown = $000D; { vt_unknown  13 } 
    varShortInt = $0010; { vt_i1   16 } 
    varByte  = $0011; { vt_ui1   17 } 
    varWord  = $0012; { vt_ui2   18 } 
    varLongWord = $0013; { vt_ui4   19 } 
    varInt64 = $0014; { vt_i8   20 } 

的另一類型(帕斯卡變種),如varStringvarAny必須檢查源和目的地TVarType並編寫您自己的測試用例。

UPDATE

作爲@大衛指出我出去,區域設置可以產生不同的結果爲相同的值,所以你必須考慮這個答案就像初始步驟或提示構建自己的功能,你必須瞭解所提議的功能中引起的區域設置問題。

+0

不錯的解決方案。不過,我認爲你應該提到['VariantChangeTypeEx'](http://msdn.microsoft.com/en-us/library/ms221634.aspx)實際上是一個OS提供的服務,因此會與本地的Delphi實現不同變體類型轉換。換句話說,這不會產生與OP基​​於異常的代碼相同的結果。 – 2011-03-03 18:59:37

+0

@David,我測試了代碼,併爲'ole兼容Variant types'工作正常,因爲我發佈代碼。我的意思是,如果函數返回'true',delphi投射應該可以正常工作。 – RRUZ 2011-03-03 19:15:31

+0

@RRUZ區域依賴轉換的處理是不同的。像整數加倍的東西顯然是相同的。但字符串<--> double可能會有不同的表現。 – 2011-03-03 19:23:13

2

我不知道內置的支持,將允許使用錯誤代碼而不是異常進行動態轉換檢查。

您可以自己手工編寫代碼,但這樣做會導致Variants單元中代碼的重複性無法容忍。在這種情況下,我認爲使用異常不如重複實現相關代碼的替代方法更糟糕。


作爲一個反例RRUZ最ingeneous的答案,我提供以下代碼:

procedure Main; 
var 
    v: Variant; 
    i: Integer; 
    CanConvert: Boolean; 
begin 
    v := '$1'; 

    Writeln(BoolToStr(TryVarAsType(v, varInteger), True)); 

    try 
    i := Integer(v); 
    if i>0 then begin 
     CanConvert := True; 
    end; 
    except 
    CanConvert := False; 
    end; 
    Writeln(BoolToStr(CanConvert, True)); 
end; 

輸出:

False 
True 
+0

對於這個downvoter,你願意提供一個反例嗎?雖然RRUZ的代碼很好,但它與OP基於異常的代碼在語義上不同。 – 2011-03-03 19:05:09

+0

同意。爲什麼要編寫try..catch圍繞你的代碼很糟糕? – 2011-03-03 19:42:29

+0

@Warren好吧,使用try/except不是很好。您希望將其包裝在低級功能中以供重複使用。當你打開「Break on Exceptions」(打破例外)時,它會造成一團亂麻。我想我的回答和評論中我有點迂腐,但我想我正在試圖以完整的概括性來考慮這個問題。對於非常窄的轉換類型,可能不會出現語言環境問題。 – 2011-03-03 19:46:58