multimethod-sharp


Multiple dispatch code generator for C#

Description

Multimethods are dynamically dispatched methods that use all of their arguments to dispatch to the best overload for the arguments they are called with. This contrasts with the more usual virtual methods where just the target of a method call is used to dispatch to an override. multimethod-sharp contains a Python script which will generate a C# multimethod for a specific signature and attach overloads to it using any methods which are decorated with an attribute nested in the multimethod class.

Multimethods are useful whenever you want to extend a class hierarchy with a virtual method without disturbing the class itself or if you want to dispatch on two or more arguments. Examples of the later include collisions, drag and drop, writing objects to different formats, etc.

However, unlike ordinary virtual methods, multimethod calls may fail at runtime. Typically this is because an overload cannot be found which is usable with the actual arguments or there is no best overload. In these cases the multimethod will throw an exception.

There's at least one other .NET library that supports multiple dispatch MultiMethods.NET but it seems to be missing some features that multimethod-sharp supports: * Support for null arguments. * Overloads may appear anywhere and don't need to have the same name as the multimethod. * Overloads can be static or instance methods (if they are instance methods then the implicit this argument should correspond to the first argument of the multimethod). * Support for calling the next-best method from within an overload (this is similar to a call to the base method from an override).

Example

Here's a simple example of how this all works out. If you want to generate a multimethod for shape intersection the python script will spit out something like:

``` // Dynamically dispatches to the best Intersects overload. internal static class Intersects { // Used to identify overloads. The overload method name may be anything // but the signature should match Invoke except that both the return and // argument types may be derived versions of the types that appear in Invoke. [Serializable] [AttributeUsage(AttributeTargets.Method, AllowMultiple = false)] internal sealed class OverloadAttribute : Attribute { }

// Calls the best overload for the actual argument types or throws System.
// MissingMethodException or System.Reflection.AmbiguousMatchException.
// Arguments may be null.
public static bool Invoke(Shape lhs, Shape rhs)
{
    // ...
}

// Calls the next best overload. For example, if overload Foo(Derived) was last
// called then this would call Foo(Base). The arguments should normally be the
// same as the ones passed into the original Invoke call. May throw System.
// MissingMethodException or System.Reflection.AmbiguousMatchException.
public static bool NextMethod(Shape lhs, Shape rhs)
{
    // ...
}

// Manually registers overload methods in the specified type. Note that if
// this is not called then all types and methods in the loaded assemblies
// are registered the first time Invoke is called.
public static void Register(Type type)
{
    // ...
}

} ```

Overloads can be registered using code like this:

``` internal abstract class Shape { }

internal sealed class Square : Shape { // An overload using an instance method. [Intersects.Overload] public bool Intersects(Square rhs) { return true; } }

internal sealed class Disk : Shape { }

// Overloads using static methods. internal static class IntersectOverloads { [Intersects.Overload] public static bool Intersects(Disk lhs, Disk rhs) { return true; } } ```

And the multimethod can be called like this:

if (Intersects.Invoke(shape1, shape2)) Console.WriteLine("interects!");

Project Information

Labels:
C .NET Mono multipledispatch multimethod multiple dispatch