Method References

Function pointers

Function pointer types can point to static methods, non-static methods with explicit ‘this’ parameters, external functions, or functions dynamically generated by libraries. Function pointers are generated with the => operator.

/* Local variable `funcPtr` */
function void() funcPtr = => StaticMethod;
funcPtr();
/* Passed as an argument to another method */
void UseFuncPtr(void() funcPtrB) => funcPtrB();
UseFuncPtr(=> StaticMethodB);
/* Note that 'ca' is not captured here. We could also specify 'ClassA.MemberMethod' */
ClassA ca = new ClassA();
function void(ClassA this, float f) funcPtr2 = => ca.MemberMethod;
funcPtr2(ca, 1.2f);
/* Note that structs need to differentiate between mut/non-mut */
StructA sa = StructA();
function void(mut StructA this, float f) funcPtr3 = => sa.MemberMethod; 
funcPtr3(mut sa, 2.3f);
/* Note that for interop, all [CRepr] structs will be passed by pointer no matter whether mut is specified or not, so calling convention is identical for both */

Delegates

Delegates are more general, and are defined as class types which can not only refer to anything a function can, but can also refer to instance methods and can hold local variable captures when they point to local methods or lambdas. Delegates are allocated using the => operator preceded by an allocation specifier.

delegate void() delegateVal = scope => MemberMethod;

Events

Events can be thought of as multicast delegates. The System.Event<T> struct wraps a delegate type and can contain zero or many delegate references.

Event<delegate void(int)> evt = default;
/* Note the use of 'new =>' because the Event takes ownership of the delegates */
evt.Add(new => obj.MethodA);
evt.Add(new => obj.MethodB);
/* This will invoke the delegates in the order they were added */
evt(intVal);
/* This removes a single delegate. Note the use of 'scope' because this argument is only used for comparison and no ownership is transferred */
evt.Remove(scope => obj.MethodA, true);
/* Dispose will delete all remaining delegates */
evt.Dispose();

Lambdas

Lambdas are a shorthand for creating a local method and then allocating a delegate to point to it, except a lambda also allows you to define a lambda destructor for freeing any resources required during the lifetime of the lambda.

static void Test(StringView str)
{
    int i = 0;

    /* Locally defined method which captures 'i' by reference */
    char8 GetNext()
    {
        if (i >= str.Length)
            return 0;
        return str[i++];
    }

    /* Locally defined method with no captures */
    char8 GetEmpty()
    {
        return 0;
    }

    /* Allocates a delegate bound to GetNext() */
    delegate char8() strDlg = scope => GetNext;

    /* Bind emptyFunc to GetEmpty(). Binding to GetNext() would fail because function pointers cannot hold captures */  
    function char8() emptyFunc = => GetEmpty;

    /* Allocate lambda */
    strDlg = scope () =>
    {
        return 'A';
    };

    /* Allocate lambda that captures by reference, which allows the GetNext call to capture 'i' */
    strDlg = scope [&] () =>
    {
        return GetNext();
    };

    /* This lambda owns a string, which gets cleaned up after lambda goes out of scope */
    String tempStr = new String(str);
    tempStr.EnsureNullTerminator();
    /* capture 'i' by reference, str and tempStr by value */
    strDlg = scope [&i, =str, =tempStr] () =>
    {
        return tempStr[i];
    }
    ~
    {
        delete tempStr;
    };
}

Valueless Method References

Valueless method references can be used to specialize certain types of generic methods such that they directly call the referenced method rather than calling indirectly through a delegate. This can improve performance in some types of critical code paths.

static T Max<T, TFunc>(T lhs, T rhs, TFunc func) where TFunc : delegate int(T lhs, T rhs)
{
    return (func(lhs, rhs) >= 0) ? lhs : rhs;
}

int CmpVec(Vector2 lhs, Vector2 rhs)
{
    return lhs.x <=> rhs.x;
}

/* The 'func' argument of this call is valueless - a specialized 'Max' method will be generated that directly calls CmpVec */
Vector2 max = Max(vec0, vec1, => CmpVec);