Browse documentation

Postfix

A postfix runs after the original completes or is skipped. It is commonly used to:

  • read or change the result of the original method
  • access the arguments of the original method
  • run even when a prefix skips the original
  • read custom state from the prefix

See the runtime flow for how prefixes, postfixes, and finalizers fit together.

Reading or changing the result

Use __result to read the result, or ref __result to change it. Its type must match the original return type or be assignable from it.

public class OriginalCode
{
    public string GetName() => name; // ...
}

[HarmonyPatch(typeof(OriginalCode), nameof(OriginalCode.GetName))]
class Patch
{
    static void Postfix(ref string __result)
    {
        if (__result == "foo")
            __result = "bar";
    }
}

Pass-through postfixes

A pass-through postfix has a non-void return type matching its first parameter's type. Harmony passes the current result to that parameter and uses the returned value as the new result. Other parameters follow normal injection rules.

This is useful for transforming an IEnumerable<T> with yield, since C# iterator methods cannot have ref parameters.

public class OriginalCode
{
    public string GetName() => "David";

    public IEnumerable<int> GetNumbers()
    {
        yield return 1;
        yield return 2;
        yield return 3;
    }
}

[HarmonyPatch(typeof(OriginalCode), nameof(OriginalCode.GetName))]
class Patch1
{
    static string Postfix(string name) => "Hello " + name;
}

[HarmonyPatch(typeof(OriginalCode), nameof(OriginalCode.GetNumbers))]
class Patch2
{
    static IEnumerable<int> Postfix(IEnumerable<int> values)
    {
        yield return 0;
        foreach (var value in values)
            if (value > 1)
                yield return value * 10;
        yield return 99;
    }
}

// will make GetNumbers() return [0, 20, 30, 99] instead of [1, 2, 3]

Reading original arguments

Use names or attributes to access original arguments and private fields. See Injections, or this example:

public class OriginalCode
{
    public void Test(int counter)
    {
        // ...
    }
}

[HarmonyPatch(typeof(OriginalCode), nameof(OriginalCode.Test))]
class Patch
{
    static void Prefix(int counter) => FileLog.Log("counter = " + counter);
}

Postfixes and skipped originals

Skipping the original does not skip postfixes. An exception from a prefix, the original, or an earlier postfix does. Use a finalizer for cleanup that must also run on failure.

Passing state between prefix and postfix

See Passing state between prefix and postfix.

Harmony 3 preview

For the stable release, read the 2.x documentation.