Generic type converting function, with a special case
Hi,
I wrote my own database handling code (actually, I write T4 text templating files that generate my database code for me) and part of it takes care of converting values from a database (as Objects) to the desired types.
I have been using this generic function successfully:
vb.net Code:
Public Overridable Function ConvertType(Of T)(value As Object) As T
Try
Return If(value IsNot Nothing AndAlso value <> DBNull.Value, DirectCast(value, T), Nothing)
Catch ex As Exception
Throw New ConversionException(GetType(T).Name, value.[GetType]().Name, ex)
End Try
End Function
It takes a type parameter (the type to convert to) and an Object (the value from the database), then checks if the value is null or DBNull, and if not the value is casted to type T.
Recently I have been adding support for SQL Server and SQLite (it used to be just MS Access) and I now need to handle Booleans as a special case. For SQLite for example, there is no boolean field so you generally use just an integer field, where 0 means False and 1 means True.
When I use my existing ConvertType method however, I will be trying to cast '1' or '0' to a Boolean, which will fail.
So, I can override this method for specific databases (Access doesn't override it as the default works, but SQLiteDatabase for example has to override it) and handle a Boolean value differently.
The idea is simply: if the type T is a Boolean and the value is an Integer, then I am assuming the database is storing an integer that represents a boolean. So, if the value is 1 I return True, if it's 0 I return False:
vb.net Code:
Public Overrides Function ConvertType(Of T)(value As Object) As T
If GetType(T) Is GetType(Boolean) AndAlso TypeOf value Is Integer Then
'Special case
If CInt(value) = 1 Then
Return True
Else
Return False
End If
Else
'Handle as usual
Return MyBase.ConvertType(Of T)(value)
End If
End Function
Simple enough, but it doesn't work... The return type of the function is (and must be) T, so I cannot return a Boolean because a Boolean cannot be converted to T!
Well... It can in this case, because I specifically check that T is Boolean, but the compiler doesn't know this so it doesn't allow me to return a Boolean. I cannot cast the boolean to T either.
Any idea's how I can solve this? I might be thinking too complex but I cannot see any solution:ehh:
Re: Generic type converting function, with a special case
You're trying to return "T", but you know that T is a boolean. That's what you want to return. Doing a DirectCast() or a CType() fails?
Re: Generic type converting function, with a special case
Got it:
Code:
If GetType(T) Is GetType(Boolean) AndAlso TypeOf value Is Integer Then
'Special case
Return DirectCast(value = 1, T)
Else
'''Usual Handling
End If
Re: Generic type converting function, with a special case
As I said, that doesn't work. It gives me a compile error: Value of type 'Boolean' cannot be converted to 'T'.
CBool doesn't work either with the same error. The problem is simple: the compiler doesn't know that T is Boolean so it doesn't know that I can convert a Boolean to T. For all it knows, T might be a Button and in that case your cast is obviously not going to work. It's not a Button because I specifically check for that, but the compiler can't know that.
Re: Generic type converting function, with a special case
This seems to work, but it's god awful:
vb.net Code:
Function ConvertType(Of T)(value As Object) As T
If GetType(T) = GetType(Boolean) AndAlso TypeOf value Is Integer Then
Return DirectCast(DirectCast(CInt(value) = 1, Object), T)
Else
Return DirectCast(value, T)
End If
End Function
No, you don't need more coffee, that's 3 casts in a row:lol: Apparently casting the boolean to an Object first works, the compiler then doesn't see that it's trying to cast a boolean to T anymore and casting Object to T does work:wave:
I'm going to leave this unresolved in case someone comes up with a better solution because this is just awful :blush:
EDIT
I should check the CIL that rolls out of this, perhaps the compiler can optimize it... I doubt I can read it though, don't know much about that.
The CIL is this;
Code:
.method public static !!T ConvertType<T>(object 'value') cil managed
{
// Code size 81 (0x51)
.maxstack 2
.locals init ([0] !!T ConvertType,
[1] bool VB$CG$t_bool$S0)
IL_0000: nop
IL_0001: ldtoken !!T
IL_0006: call class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle)
IL_000b: ldtoken [mscorlib]System.Boolean
IL_0010: call class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle)
IL_0015: call bool [mscorlib]System.Type::op_Equality(class [mscorlib]System.Type,
class [mscorlib]System.Type)
IL_001a: brfalse.s IL_0024
IL_001c: ldarg.0
IL_001d: isinst [mscorlib]System.Int32
IL_0022: brtrue.s IL_0027
IL_0024: ldc.i4.0
IL_0025: br.s IL_0028
IL_0027: ldc.i4.1
IL_0028: stloc.1
IL_0029: ldloc.1
IL_002a: brfalse.s IL_0044
IL_002c: ldarg.0
IL_002d: call int32 [Microsoft.VisualBasic]Microsoft.VisualBasic.CompilerServices.Conversions::ToInteger(object)
IL_0032: ldc.i4.1
IL_0033: ceq
IL_0035: box [mscorlib]System.Boolean
IL_003a: unbox.any !!T
IL_003f: stloc.0
IL_0040: br.s IL_004f
IL_0042: br.s IL_004e
IL_0044: nop
IL_0045: ldarg.0
IL_0046: unbox.any !!T
IL_004b: stloc.0
IL_004c: br.s IL_004f
IL_004e: nop
IL_004f: ldloc.0
IL_0050: ret
} // end of method Module1::ConvertType
Gibberish to me...
Re: Generic type converting function, with a special case
I don't really think I need to explain since you'll know what it's doing... but, since you know that T is a Boolean you can actually nest another call to ConvertType. If you do this and pass Boolean values, it will instead use the default implementation (your base class implementation) to convert a Boolean to a Boolean.
Code:
'//methods
Public Overrides Function ConvertType(Of T)(ByVal value As Object) As T
'//special case
If GetType(T) Is GetType(Boolean) AndAlso _
TypeOf value Is Integer Then
'//any non-zero value is the definition of true
Return MyBase.ConvertType(Of T)(CInt(value) <> 0)
End If
'//handle as usual
Return MyBase.ConvertType(Of T)(value)
End Function
Edit: Here's the C# since I know that's what you use:
Code:
public T ConvertType<T>(object value)
{
if (typeof(T) == typeof(bool) && value is int)
{
return base.ConvertType<T>((int)value != 0);
}
return base.ConvertType<T>(value);
}
Re: Generic type converting function, with a special case
Cool, that's pretty clever! I didn't think of that at all. I think the meaning is clearer to just call the base implementation directly though (no need to do the check again because we know this time value is not an integer).
As for C#, I'm using both actually, I'm writing my database library in VB and C# at the same time (though VB version is a little behind...).
Re: Generic type converting function, with a special case
Quote:
Originally Posted by
NickThissen
just call the base implementation directly though
My bad, I copy my source into an application I developed to highlight so it was using the pre-changed version of that. I will update that post. Anyway, I hope that resolves it for you.
Re: Generic type converting function, with a special case
Quote:
Originally Posted by
NickThissen
As I said, that doesn't work. It gives me a compile error: Value of type 'Boolean' cannot be converted to 'T'.
CBool doesn't work either with the same error. The problem is simple: the compiler doesn't know that T is Boolean so it doesn't know that I can convert a Boolean to T. For all it knows, T might be a Button and in that case your cast is obviously not going to work. It's not a Button because I specifically check for that, but the compiler can't know that.
Weird...it worked for me. Oh well...but I'm glad FA got it to work :)