dotnet / dotnet/vblang

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

Open
#59 23 comments 18 reactions 0 assignees View on GitHub
Proposal
Dominant language
No language data
Stars
328
Forks
71
PR merge metrics
No merged PRs in 30d

Description

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](https://user-images.githubusercontent.com/9663150/32391272-c8c8190e-c0a7-11e7-9e45-419b70d784e2.gif)

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
IL_000f: unbox.any valuetype [mscorlib]System.Nullable`1
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)
```

Contributor guide

No contributing guide indexed for this repository

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.