Want to protect your cyber security and still get fast solutions? Ask a secure question today.Go Premium

x
  • Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 2277
  • Last Modified:

Casting by a generic type (T) is not the same as casting as the actual type?

Hello,

I am playing with generics and nullable types in VB.NET. I'm finding that CType behaves differently than I expected it to when passed a generic type parameter as an argument. The attached code snipit returns the following results:

CType(data, Short?) = 1
Convert(Of Short?)(data) = Specified cast is not valid.
Press any key...

Can anyone tell me why this is? Surely, there is a way around this. Can you suggest a better way to accomplish what I'm doing here?

Also, I have duplicated this behavior in C# and that's why I have posted this question in both topic areas.

Thanks.
-Jason

'VB.NET
Module Module1
 
    Sub Main()
        Dim data As Byte = 1
 
        Dim actual As Short? = CType(data, Short?)
        Console.WriteLine("CType(data, Short?) = {0}", actual)
 
        Try
            actual = Convert(Of Short?)(data)
            Console.WriteLine("Convert(Of Short?)(data) = {0}", actual)
        Catch ex As Exception
            Console.WriteLine("Convert(Of Short?)(data) = {0}", ex.Message)
        End Try
 
        Console.WriteLine("Press any key...")
        Console.ReadKey()
    End Sub
 
    Function Convert(Of T)(ByVal data As Object) As T
        If (data Is Nothing OrElse data Is DBNull.Value) Then
            Return Nothing
        Else
            Return CType(data, T)
        End If
    End Function
End Module
 
 
 
 
//C#
    class Program
    {
        static void Main(string[] args)
        {
            byte data = 1;
 
            short? actual = (short?)data;
            Console.WriteLine("(short?)data = {0}", actual);
 
            try
            {
                actual = Convert<short?>(data);
                Console.WriteLine("Convert<short?>(data) = {0}", actual);
            }
            catch (Exception ex)
            {
                Console.WriteLine("Convert<short?>(data) = {0}", ex.Message);
            }
 
            Console.WriteLine("Press any key...");
            Console.ReadKey();
        }
 
        private static T Convert<T>(object data)
        {
            if (data == null || data == DBNull.Value)
            {
                return default(T);
            }
            else
            {
                return (T)data;
            }
        }
    }

Open in new window

0
whatsit2002
Asked:
whatsit2002
  • 5
  • 3
1 Solution
 
Mike TomlinsonMiddle School Assistant TeacherCommented:
Interesting...

It works, if you do it WITHOUT the Nullable character in the call to Convert().

This:

    S = Convert(Of Short)(data)

Compared To:

    S = Convert(Of Short?)(data)
Public Class Form1
 
    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
        Dim data As Byte = 1
 
        Dim S As Short?
        If S.HasValue() Then
            Debug.Print("S = " & S.Value.ToString)
        Else
            Debug.Print("S has No Value Assigned")
        End If
 
        S = MyConvert(Of Short)(data)
        If S.HasValue() Then
            Debug.Print("S = " & S.Value.ToString)
        Else
            Debug.Print("S has No Value Assigned")
        End If
    End Sub
 
    Function MyConvert(Of T)(ByVal data As Object) As T
        If (data Is Nothing OrElse data Is DBNull.Value) Then
            Return Nothing
        Else
            Return CType(data, T)
        End If
    End Function
 
End Class

Open in new window

0
 
drichardsCommented:
C# seems to be a bit more consistent.  It doesn't with with Convert<short?> OR Convert<short>.  It DOES work, however with either <byte?> or <byte> with data defined as byte.  You cannot unbox the object to something other than the original type with a direct cast.  MS docs say this about unboxing conversion:

"For the unboxing of value types to succeed at run time, the item being unboxed must be a reference to an object that was previously created by boxing an instance of that value type"

You can cast to IConvertible and use To<Type>() methods, which is what CType compiles to in this case according to the Reflector utility.  Hence the slightly different behavior in VB.  Unfortunately, there is no case in IConvertible for the nullable versions of the types, so that doesn't work.

Seems like incomplete support for nullable types in the framework.  Would be interesting to hear Microsoft's take on that.
0
 
whatsit2002Author Commented:
Idle_Mind: I agree that Convert(Of Short)(data) works fine. I was tempted to work around the issue like that. However, that is not a good solution, because I need null/Nothing returned is certain cases.

drichards: Thanks for the info. I'm still investigating this, though. I'm confused about why a straight cast like (short?)data works fine, but not in a generic function; using a generic type parameter. I am starting to agree with you that this might be a shortcoming in the .NET framework. I'll see if I can figure out a way to get info out of MS about this.

Thanks for your continued comments. I'm going to leave the question open for a little while so that others can chime in if they want.

Thanks.
-Jason
0
Nothing ever in the clear!

This technical paper will help you implement VMware’s VM encryption as well as implement Veeam encryption which together will achieve the nothing ever in the clear goal. If a bad guy steals VMs, backups or traffic they get nothing.

 
drichardsCommented:
>> I'm confused about why a straight cast like (short?)data works fine, but not in a generic function
It doesn't work.  That's what I was trying to explain.  The CType function is compiled differently depending on context.  If you try the same cast in C# (boxed object containing byte to short?) it wont work.  Add the lines:

    object o = data;
    actual = (short?)o;

to your C# version after the line:

    short? actual = (short?)data;

The cast of o will throw the cast exception also.
0
 
drichardsCommented:
To finish that last thought...

If you use the Reflector tool, it will show you that in the first case in your VB it compiles to a direct cast and looks like C# cast.  In the second case (in Convert function), it compiles to a case statement with a To<Type>() call (basically IConvertible), and there is no case for the nullable types.

You can get the reflector here (http://www.red-gate.com/products/reflector/index.htm) if you don't have it.  It's very useful in many cases.
0
 
drichardsCommented:
And to be very clear, the straight cast on (byte data) works because it os converting a basic type and not having to unbox an object.  Byte CAN be converted directly to short?, but the boxed byte cannot because of the limitation that unboxing must be to exactly the same type that is boxed.  No "extra" conversion can take place until the value is unboxed.
0
 
whatsit2002Author Commented:
Alright, I've got my mind around what you are saying, now. It is the boxing into type object and then unboxing that is causing the problems; not the conversion of the actual types themselves.

Thanks for the clarification.
-Jason
0
 
drichardsCommented:
Yes, exactly.  Not sure where that leaves you with your original problem, but it was a very interesting question.
0
 
whatsit2002Author Commented:
Thanks to you both for your time and information.
-Jason
0

Featured Post

VIDEO: THE CONCERTO CLOUD FOR HEALTHCARE

Modern healthcare requires a modern cloud. View this brief video to understand how the Concerto Cloud for Healthcare can help your organization.

  • 5
  • 3
Tackle projects and never again get stuck behind a technical roadblock.
Join Now