Skip to content

Proposal: Let's fix casting, unboxing and converting in VB with "As Type" #59

@KlausLoeffelmann

Description

@KlausLoeffelmann

This idea addresses scenarios #82, #83, #84, and #87.

UPDATE: This is an animated Gif of a prototype which demonstrates the feature in more detail.

astype

Casting, Boxing/Unboxing and type converting in Visual Basic is currently an inconsistent mess. We have TryCast, DirectCast, CType and Casting/Converting Keywords based on individual types (CInt, CString, CChar, CShort, CUShort, CByte, CSByte etc.). So, folks often do not know, which Operator to use in what situation, particularly since CType and DirectCast generate not only different code, the CType code is also considerable slower, for example when unboxing value types. Only few VB developers now this:

Unboxing Integer Value CType vs. DirectCast:
DirectCast:

intvalue = DirectCast(objectInt, Integer)
Resulting IL:

IL_002c: ldloc.2
IL_002d: unbox.any [mscorlib]System.Int32
IL_0032: stloc.1

CType:

intvalue = CType(objectInt, Integer)

Resulting IL:

IL_0079: ldloc.2
IL_007a: call int32 [Microsoft.VisualBasic]Microsoft.VisualBasic.CompilerServices.Conversions::ToInteger(object)
IL_007f: stloc.1

On top, DirectCast and CType make it hard to read and write VB code for CSharp developers, and also for VB Developers whose teams prefer CType over DirectCast or vice versa. Apart from aesthetics and differences which lead to differently performing code (up to factor 2,5!), what’s really bad is this: Although there are way too many Casting/Converting/Unboxing operators, a few scenarios cannot even be applied at all in Visual Basic.

#1: Pure conversion from Double/Float in Int16/32/64 is not possible in VB.
C# Sample:

            double doubleVar = 3.14159;
            int inVar = (int)(doubleVar / 42);

Resulting IL:

  IL_0001:  ldc.r8     3.1415899999999999
  IL_000a:  stloc.0
  IL_000b:  ldloc.0
  IL_000c:  ldc.r8     42.
  IL_0015:  div
  IL_0016:  conv.i4
  IL_0017:  stloc.1

VB Sample:

        Dim doubleVar As Double = 3.14159
        Dim intVar = CInt(doubleVar / 42)

Resulting IL:

IL_0000:  nop
IL_0001:  ldc.r8     3.1415899999999999
IL_000a:  stloc.0
IL_000b:  ldloc.0
IL_000c:  ldc.r8     42.
IL_0015:  div
IL_0016:  call       float64 [mscorlib]System.Math::Round(float64)
IL_001b:  conv.ovf.i4

There seems to be no way in VB to not make a trip to Math.Round in the conversion from floating point to integer. This is most probable to keep legacy behavior from VB6 code.

#2: Convert an integer to a character with the specified value.
CSharp Code:

            var charVar = (char)65;

Resulting IL:

  .locals init ([0] float64 doubleVar,
           [1] int32 inVar,
           [2] char charVar,
           [3] string stringVar)
.
.
.
  IL_0018:  ldc.i4.s   65
  IL_001a:  stloc.2

VB Code:

        'Error:
        'Dim charVar = CChar(65)
        'Dim charVar = CType(65, Char)
        'Only way:
        Dim charVar = Convert.ToChar(65) ' or, of course, ChrW(65) - thx Bill!

#3: TryCast Unboxing to Nullable Value Types.
CSharp Code:

            object iAso = 42;
            var iN = iAso as int?;

Resulting IL:

  IL_0001:  ldc.i4.s   42
  IL_0003:  box        [mscorlib]System.Int32
  IL_0008:  stloc.0
  IL_0009:  ldloc.0
  IL_000a:  isinst     valuetype [mscorlib]System.Nullable`1<int32>
  IL_000f:  unbox.any  valuetype [mscorlib]System.Nullable`1<int32>
  IL_0014:  stloc.1

Visual Basic Code:

        Dim iAso As Object = 42
        'Does not work!
        'Dim iN = TryCast(iAso, Integer?)

Proposed Solution:
Let’s introduce As Type as a universal conversion, unboxing and TryCast Operator that would work similar to C# casting (type) and As Type. This way, we would not break any existing code. Adding As at the end of an expression currently produces a syntax error, so this new usage should not break any existing code. On top, ForEach loops already allow As Type, so this usage would become more consistent and would feel kind of right. Here are a few examples:

        ‘Conversion, and we do not need to take legacy into account
        Dim doubleVar As Double = 3.14159
        Dim intVar = (doubleVar / 42) As Integer

        ‘ In contrast to CType however, As Type would result in a TryCast…
        Dim c As Contact = New Employee With
            {.FirstName = "Adriana",
             .LastName = "Löffelmann",
             .Employer = "ActiveDevelop"
            }

        Dim e = c As Employee ' TryCast(c, Employee), same as in CSharp!
 
        ‘…to be consistent with Try-Cast-Unboxing, which was not possible before:
        Dim iAso As Object = 42
        Dim iNull = iAso As Integer?

       'we could even leave this „bug“ as it is, because this line would be easily understood:
        Dim iNull = If(Nothing, 1 As Integer?, Nothing)

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions