AssignVariant (and FromVariant, which calls it) does something different in each nullable type when it gets a value it cannot convert. Some raise, some just become invalid, the exception classes differ, and only TNullableDate documents what it does. Measured with Delphi 12 Win32 ("invalid" means no exception and IsValid False afterwards):
| Null, Empty | 42 |
'abc' |
VarArrayOf([1, 2]) |
1e300 |
NaN |
|
|---|---|---|---|---|---|---|
| TNullableDate | invalid | valid | EConvertError |
EConvertError |
EConvertError |
EConvertError |
| TNullableDateTime | invalid | valid | invalid | invalid | invalid | invalid |
| TNullableTime | invalid | invalid | invalid | invalid | invalid | invalid |
| TNullableString | invalid | valid | valid | EVariantConvertError |
valid | valid |
| TNullableInt32 | invalid | valid | EVariantConvertError |
EVariantConvertError |
EVariantConvertError |
EVariantConvertError |
| TNullableInt64 | invalid | valid | EVariantConvertError |
EVariantConvertError |
valid (*) | valid (*) |
| TNullableDouble | invalid | valid | EVariantConvertError |
EVariantConvertError |
valid | valid |
| TNullableUInt32 | invalid | valid | EVariantConvertError |
EVariantConvertError |
invalid | invalid |
| TNullableUInt64 | invalid | valid | invalid | EVariantConvertError |
invalid | invalid |
| TNullableGuid | invalid | invalid | invalid | EVariantConvertError |
invalid | invalid |
(*) with a wrong value; on Delphi 2007 the 1e300 and NaN cells of Int32, Int64, UInt32 and UInt64 raise EInvalidOp instead. Both come from the TryVar2Xxx functions underneath, see [bugs:#82].
So:
TNullableDate raises EConvertError (it is the only one with an ErrorHandling parameter), the numeric types and TNullableString raise EVariantConvertError for some values, TNullableDateTime, TNullableTime and TNullableGuid never raise for these values, and TNullableUInt64 raises for an array but not for 'abc'.TNullableGuid.AssignVariant raises for an array although it is a function that returns whether the value was valid.TNullableTime only accepts a string in ISO format, the form its ToVariant writes. A varDate, which is what a TIME field's Value is, becomes invalid, while TNullableDateTime and TNullableDate accept varDate and numbers.A caller cannot write x.AssignVariant(Field.Value) without knowing which of these behaviours the type has, and nothing tells them.
Expected: one contract for all of them, documented. One option is the one TNullableDate already has: an ErrorHandling parameter, EConvertError with ehRaiseException, invalid and False with ehNoException. Changing the default of the types that never raise today would break callers that rely on that, so their default may have to stay ehNoException.