Results 1 to 9 of 9

Thread: Generic type converting function, with a special case

  1. #1

    Thread Starter
    PowerPoster
    Join Date
    Apr 2007
    Location
    The Netherlands
    Posts
    5,070

    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:
    1. Public Overridable Function ConvertType(Of T)(value As Object) As T
    2.     Try
    3.         Return If(value IsNot Nothing AndAlso value <> DBNull.Value, DirectCast(value, T), Nothing)
    4.     Catch ex As Exception
    5.         Throw New ConversionException(GetType(T).Name, value.[GetType]().Name, ex)
    6.     End Try
    7. 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:
    1. Public Overrides Function ConvertType(Of T)(value As Object) As T
    2.     If GetType(T) Is GetType(Boolean) AndAlso TypeOf value Is Integer Then
    3.                 'Special case
    4.         If CInt(value) = 1 Then
    5.             Return True
    6.         Else
    7.             Return False
    8.         End If
    9.     Else
    10.                 'Handle as usual
    11.         Return MyBase.ConvertType(Of T)(value)
    12.     End If
    13. 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

  2. #2

    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?

  3. #3

    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

  4. #4

    Thread Starter
    PowerPoster
    Join Date
    Apr 2007
    Location
    The Netherlands
    Posts
    5,070

    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.

  5. #5

    Thread Starter
    PowerPoster
    Join Date
    Apr 2007
    Location
    The Netherlands
    Posts
    5,070

    Re: Generic type converting function, with a special case

    This seems to work, but it's god awful:
    vb.net Code:
    1. Function ConvertType(Of T)(value As Object) As T
    2.         If GetType(T) = GetType(Boolean) AndAlso TypeOf value Is Integer Then
    3.             Return DirectCast(DirectCast(CInt(value) = 1, Object), T)
    4.         Else
    5.             Return DirectCast(value, T)
    6.         End If
    7.     End Function
    No, you don't need more coffee, that's 3 casts in a row 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

    I'm going to leave this unresolved in case someone comes up with a better solution because this is just awful

    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...

  6. #6
    Master Of Orion ForumAccount's Avatar
    Join Date
    Jan 2009
    Location
    Canada
    Posts
    2,802

    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);
    }
    Last edited by ForumAccount; Aug 2nd, 2011 at 10:39 AM. Reason: Was using old version

  7. #7

    Thread Starter
    PowerPoster
    Join Date
    Apr 2007
    Location
    The Netherlands
    Posts
    5,070

    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...).

  8. #8
    Master Of Orion ForumAccount's Avatar
    Join Date
    Jan 2009
    Location
    Canada
    Posts
    2,802

    Re: Generic type converting function, with a special case

    Quote Originally Posted by NickThissen View Post
    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.

  9. #9

    Re: Generic type converting function, with a special case

    Quote Originally Posted by NickThissen View Post
    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

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  



Click Here to Expand Forum to Full Width