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

LVL 7
whatsit2002Asked:
Who is Participating?
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

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
Get expert help—faster!

Need expert help—fast? Use the Help Bell for personalized assistance getting answers to your important questions.

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

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
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
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
.NET Programming

From novice to tech pro — start learning today.

Question has a verified solution.

Are you are experiencing a similar issue? Get a personalized answer when you ask a related question.

Have a better answer? Share it in a comment.