2011-03-03 211 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