Dynamic method calling in VB without reflection
Asked Answered
G

3

6

I want to format any numeric type using a method call like so:

Option Infer On
Option Strict Off
Imports System.Runtime.CompilerServices

Namespace GPR
    Module GPRExtensions
        <Extension()>
        Public Function ToGPRFormattedString(value) As String
            ' Use VB's dynamic dispatch to assume that value is numeric
            Dim d As Double = CDbl(value)
            Dim s = d.ToString("N3")
            Dim dynamicValue = value.ToString("N3")
            Return dynamicValue
        End Function
    End Module
End Namespace

Now, from various discussions around the web (VB.Net equivalent for C# 'dynamic' with Option Strict On, Dynamic Keyword equivalent in VB.Net?), I would think that this code would work when passed a numeric type (double, Decimal, int, etc). It doesn't, as you can see in the screenshot:

Exception showing dynamic method call failing

I can explicitly convert the argument to a double and then .ToString("N3") works, but just calling it on the supposedly-dynamic value argument fails.

However, I can do it in C# with the following code (using LINQPad). (Note, the compiler won't let you use a dynamic parameter in an extension method, so maybe that is part of the problem.)

void Main()
{
    Console.WriteLine (1.ToGPRFormattedString());
}

internal static class GPRExtensions
{
    public static string ToGPRFormattedString(this object o)
    {
        // Use VB's dynamic dispatch to assume that value is numeric
        var value = o as dynamic;
        double d = Convert.ToDouble(value);
        var s = d.ToString("N3").Dump("double tostring");
        var dynamicValue = value.ToString("N3");
        return dynamicValue;
    }
}

Dynamic method invocation in C# works

So what gives? Is there a way in VB to call a method dynamically on an argument to a function without using reflection?

Gagarin answered 23/7, 2013 at 20:3 Comment(0)
A
5

To explicitly answer "Is there a way in VB to call a method dynamically on an argument to a function without using reflection?":

EDIT: I've now reviewed some IL disassembly (via LinqPad) and compared it to the code of CallByName (via Reflector) and using CallByName uses the same amount of Reflection as normal, Option Strict Off late binding.

So, the complete answer is: You can do this with Option Strict Off for all Object references, except where the method you're trying exists on Object itself, where you can use CallByName to get the same effect (and, in fact, that doesn't need Option Strict Off).

Dim dynamicValue = CallByName(value, "ToString", CallType.Method, "N3")

NB This is not actually the equivalent to the late binding call, which must cater for the possibility that the "method" is actually a(n indexed) property, so it actually calls the equivalent of:

Dim dynamicValue = CallByName(value, "ToString", CallType.Get, "N3")

for other methods, like Double.CompareTo.


Details

Your problem here is that Object.ToString() exists and so your code is not attempting any dynamic dispatch, but rather an array index lookup on the default String.Chars property of the String result from that value.ToString() call.

You can confirm this is what is happening at compile time by trying value.ToString(1,2), which you would prefer to attempt a runtime lookup for a two parameter ToString, but in fact fails with

Too many arguments to 'Public ReadOnly Default Property Chars(index As Integer) As Char'

at compile time.

You can similarly confirm all other non-Shared Object methods are called directly with callvirt, relying upon Overrides where available, not dynamic dispatch with calls to the Microsoft.VisualBasic.CompilerServices.NewLateBinding namespace, if you review the compiled code in IL.

Akene answered 11/2, 2014 at 1:11 Comment(2)
I don't quite follow. I know that ToString exists on Object - that's why I don't want my variable to be of type Object but of type dynamic. This is possible in C#, but doesn't seem to work in VB.Gagarin
Yeah, that's the point you're making. Then I'm just clarifying why VB's existing concept of Object as dynamic is failing for you here.Akene
P
0

You are using a lot of implicit typing, and the compiler doesn't appear to be assigning the type System.Dynamic to the variables you want to be dynamic.

You could try something like:

Option Infer On
Option Strict Off
Imports System.Runtime.CompilerServices

Namespace GPR
    Module GPRExtensions
        <Extension()>
        Public Function ToGPRFormattedString(value as System.Dynamic) As String
            ' Use VB's dynamic dispatch to assume that value is numeric
            Dim d As Double = CDbl(value)
            Dim s = d.ToString("N3")
            Dim dynamicValue as System.Dynamic = value.ToString("N3")
            Return dynamicValue
        End Function
    End Module
End Namespace
Psychologize answered 23/7, 2013 at 23:43 Comment(0)
E
0
Option Infer On
Option Strict Off
Imports System.Runtime.CompilerServices

Namespace GPR
    Module GPRExtensions
        <Extension()>
        Public Function ToGPRFormattedString(value) As String
            Dim dynamicValue = FormatNumber(CDbl(value), 3)
            Return dynamicValue
        End Function
    End Module
End Namespace
Engelhart answered 16/8, 2018 at 6:58 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.