Dynamic method calling in VB without reflection
dynamic, vb.net
Solution
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.
Problem
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: 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; } } ``` So what gives? Is there a way in VB to call a method dynamically on an argument to a function without using reflection?