This project has moved and is read-only. For the latest updates, please go here.

Method overload matching process/algorithm

Mar 11, 2014 at 6:03 AM
There is a "Matching process" that finds the best method overload from a provided list against provided values in class 'TryInvokeWithValuesExtensions'.

Why don't we have this logic abstracted in some other extension methods like 'MethodMatchExtensions' to make them reusable.

Or do we have such a function in other places of the library that I can use?

Mar 11, 2014 at 2:17 PM
I suppose the best reason for it not being public is that we didn't want to document and support the internals - the code is a bit hairy ;)

You could grab the Fasterflect source and change the types in Extensions/Services/Probing/*.cs to public (or look them up using Fasterflect; they're mostly internal). The MapFactory class is what you'd want to use as a starting point - it has methods to produce the invocation helper classes (MethodMap and ConstructorMap), which store information on where and how the supplied parameters should be used.

If you have a usage scenario where it makes sense for us to make them public, I suppose we could consider that.
Mar 11, 2014 at 7:10 PM
Well, actually we do.

We want to:
  1. discover the most appropriate method based on input values to execute
  2. Generate Delegate for the method and cache
  3. invoke delegate
That will be a replacement for MethodInfo.InvokeMember when the member is a method.
Mar 11, 2014 at 8:25 PM
Edited Mar 11, 2014 at 8:29 PM
That is actually pretty much what Fasterflect is supposed to do internally, although it is true that using a locally cached delegate will be slightly faster (and somewhat faster if you're not using .NET 4 or newer).

That said, it looks like Fasterflect only caches the invocation maps for constructors. I'll need to dive deeper into the code to remember if there was a reason for this, since clearly method invocations could also benefit from being cached (figuring out which method is the best match is quite costly). I think all that's needed to give repeated invocations a significant boost is to enable the map cache, so that Fasterflect doesn't recompute the best match for every invocation.

I don't think it would make sense to add extensions to return a delegate directly, because the internals of the matching logic are quite forgiving. For instance, the best possible match might order parameters differently or require some parameter values to be type converted before the delegate is invoked. Without this additional processing logic, the generated delegate will fail if you just pass it the row of parameters (unless the map was a "perfect match"). However, the map itself caches the generated delegate, so it might make sense to expose the MethodMap class and the methods to produce it - so you could simply cache the map locally and use it to perform repeated invocations.

With this information at hand, do you think it would be sufficient if Fasterflect was fixed to cache the generated MethodMaps internally?
Mar 11, 2014 at 8:48 PM
That is right, you only cache on ConstructorInfo. The caching on MethodInfo is an absolutely what I want. At the moment if you write a simple test to use TryCallMethodWithValues you can see the problem. It is very slow (event slower that MethodBase.InvokeMember). To solve that issue I wanted to have MethodInfo resolve by values and then I could cache on delegate to solve performance problem.

So I think, if you fix the cache problem, that solves my issue.
Mar 11, 2014 at 9:11 PM
I think the problem is most likely to have been hash clashing, that is, two separate method maps yielding the same hash. For performance reasons the cache doesn't check the map returned from the cache lookup, so if there were clashes the first map generated would be used.

I'll probably need to write some code to work around that problem before I can enable the cache, or people will get weird exceptions (eventually).

I'll post a message here when there is some new commit for you to test.
Mar 12, 2014 at 3:51 AM
I had a look at the code, but it appears that Buu has been extending the logic in TryInvokeWithValues - to the point where it hardly feels like familiar territory. I've therefore not made any modifications to those extension methods.

However, it turns out that Fasterflect also has a MethodDispatcher class, which sounds like it fits the bill exactly. I've enhanced it a bit, so that best-match maps are cached inside the MethodDispatcher, and also made it a bit easier to work with (previously you had to manually add candidate methods; now you can pass a type on the constructor and all instance methods on that type will be used as candidates for matching).

There are some test cases illustrating possible uses of the class (see Probing/MethodDispatcherTest.cs in FasterflectTest).

Let me know if this works for you.
Mar 12, 2014 at 12:53 PM
Well, sort of yes/no. How does MethodDispatcher replicates MethodInfo.InvokeMember ?

I do not have type of parameters, what I have is method name, a supplied/target object, that can be null as well or might be a Type (static methods are needed).

I am not a bit lost how to get fasterflect use in this scenario.
Mar 12, 2014 at 2:37 PM
Edited Mar 12, 2014 at 2:41 PM
Okay, now I'm a bit confused.. MethodInfo.InvokeMember is not a Fasterflect method - instead, it is a built-in mechanism to execute a method that you have previously looked up using reflection. When you invoke it, you need to pass it exactly the right arguments of the right types and in the right order.

The whole point of the MethodDispatcher / TryCall* functionality is that you have multiple choices available, and which method to call should be determined from the list of available parameters.

For instance, you could use the MethodDispatcher like this:
var dispatcher = new MethodDispatcher();
// add all method overloads for the method "SomeMethod" to the dispatcher
typeof(SomeClass).Methods( "SomeMethod", Flags.InstanceAnyVisibility ).ForEach( dispatcher.AddMethod );

// invoke the most appropriate method that takes both a "count" and a "name" parameter
dispatcher.Invoke( someObjectOfTypeSomeClass, true, new { count = 1, name = "foo" } );
// this would match methods defined like this:
// private void SomeMethod( string name, double count ) { ... }
// public void SomeMethod( long count, string name ) { ... }
// void SomeMethod( string name, int count ) { ... }

// invoke the most appropriate method that takes either or both a "count" and a "name" parameter
dispatcher.Invoke( someObjectOfTypeSomeClass, false, new { count = 1, name = "foo" } );
// this would match methods defined like this (in addition to all of the above):
// private void SomeMethod( string name ) { ... }
// public void SomeMethod( long count ) { ... }

// invoke using a dictionary of parameter values
var parameters = new Dictionary<string,object>{ {"count",1}, {"name","foo"} };
dispatcher.Invoke( someObjectOfTypeSomeClass, true, parameters );
// this would match the same methods as the first .Invoke above
If you ignore for a moment that MethodDispatcher does not support static methods (easy to fix), is this what you are looking for?

Otherwise you'll have to explain in a bit more detail what problem you're trying to solve and what information you have at hand for it.
Mar 16, 2014 at 10:38 PM
I've added support for static methods to MethodDispatcher..

Anyway, since I haven't heard back from you I'm going to assume that you resolved your problems somehow.