vblang icon indicating copy to clipboard operation
vblang copied to clipboard

[T(Index) Is Nothing] is [CObj(T(Index)) Is Nothing] instead [T(Index) Is Default].

Open RevensofT opened this issue 4 years ago • 5 comments

    Public Function empty_index(Of T)(Host As T()) As Long
        Dim Length = Host.LongLength,
            Pos = 0L
        Do
            If Pos >= Length Then Return -1

            'Not work with value type.
            If Host(Pos) Is Nothing Then Return Pos

            'Because it's compile to this.
            If CObj(Host(Pos)) Is Nothing Then Return Pos

            'While this work well with value type and reference type.
            If Object.Equals(Input, CType(Nothing, T)) Then Return Pos

            Pos += 1
        Loop
    End Function

Why not Object.Equals ?

Simple answer is super cut corner for performance as you see in IL code below.

ldarg.0
ldloc.2
conv.i4
ldelem     !!T
box        !!T
// call       bool [System.Runtime]System.Object::Equals(object,object)
brtrue.s   IL_0023

Instead call Object.Equals it use 0 aka null pointer compare result so when brtrue which is branch on true and true in this case is anything except 0.

How to fixed it ?

Just reverse back to use Object.Equals.

    <Extension>
    Public Function is_default(Of T)(Input As T) As Boolean
        Return Object.Equals(Input, CType(Nothing, T))
    End Function

Or double down it, cut more corner.

    .method public static bool is_default<T>(!!T Input) cil managed
    {
        .custom instance void [System.Runtime]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( 01 00 00 00 )
        .maxstack  2

        ldarg.0 
        ldc.i4.0 
        ceq 
        ret
    }

RevensofT avatar Jul 27 '21 06:07 RevensofT

There is nothing to be fixed. What you really want is a proper "Is Default" test - something that can tell us if a reference or value-type expression is just all of its bits set to 0. That's what "default" means, in the .NET sense. All 0's is a null for a reference type or a zero for (typical) numeric types or what the default of any given Structure will be.

rskar-git avatar Jul 27 '21 22:07 rskar-git

@rskar-git , How would you check is value of generic type is default in VB.Net ? Is in VB.net can check reference type only and Default only use with Property, Nothing in VB.net is default value of type but in this case we can't use Is or = to check it that why it become problem and we have to fixed this to get result.

Compiler programmer is box !!T to fixed it but not solve all problem, when it is reference type it work fine because boxing null reference will return null reference back but when its type is value type, boxing it won't return null reference or default of those type back instead it will return boxed of value which not count as null reference or 0 so it make VB.net can't check default value of generic type normally via language feature so I need to fixed by use Object.Equals(Input, CType(Nothing, T)) or cut more corner in IL code.

RevensofT avatar Jul 28 '21 04:07 RevensofT

How would you check is value of generic type is default in VB.Net ? ...

For an unconstrained T, where you want the position of a null for references or default for value-types or default of a value-type boxed as an Object, then maybe:

Function empty_index(Of T)(Host As T()) As Long

    Dim Length = Host.LongLength,
                Pos = 0
    Do
        If Pos >= Length Then Return -1

        If GetType(T).IsValueType Then
            If Host(Pos).Equals(DirectCast(Nothing, T)) Then Return Pos
        Else
            If Host(Pos) Is Nothing Then Return Pos
            If Host(Pos).GetType().IsValueType Then
                If Host(Pos).Equals(DirectCast(Nothing, T)) Then Return Pos
            End If
        End If

        Pos += 1
    Loop

End Function

rskar-git avatar Jul 28 '21 20:07 rskar-git

With so many if and type access info I think those cut corner won't make it better then just use Object.Equals; anyway it sad to see VB.net language syntax can't even to keep up very old feature like generic type, I feel like many old syntax still stuck in .Net 2.0 era.

RevensofT avatar Jul 29 '21 16:07 RevensofT

Nicer code:

Function empty_index(Of T)(Host As T()) As Long

    Dim Length = Host.LongLength,
                Pos = 0
    Do
        If Pos >= Length Then Return -1

        If EqualityComparer(Of T).Default.Equals(Host(Pos), Nothing) Then
            ' See https://stackoverflow.com/questions/65351/null-or-default-comparison-of-generic-argument-in-c-sharp
            Return Pos
        End If

        Pos += 1
    Loop

End Function

rskar-git avatar Jul 30 '21 00:07 rskar-git