Show / Hide Table of Contents

Patching

Finalizer

A finalizer is a method that makes Harmony wrap the original and all other patches in a try/catch block. It can receive a thrown exception and even suppress it or return a different one.

It is a very good candidate for code that has to run regardless of what happens. Its counterpart is a Prefix with no side effects (void return type and no ref/out arguments). These are never skipped and thus serve as a way to run code guaranteed at the start of a method.

Finalizers are commonly used to:

  • suppress exceptions
  • remap exceptions
  • make sure your code is always executed

Suppressing any exceptions

To suppress all exceptions, return null from a finalizer with return type Exception. This prevents any exception from being rethrown.

public class OriginalCode
{
    public void MightFail()
    {
        throw new Exception("fail");
    }
}

[HarmonyPatch(typeof(OriginalCode), nameof(OriginalCode.MightFail))]
class Patch
{
    static Exception Finalizer()
    {
        return null; // suppresses all exceptions
    }
}

Observing exceptions

To observe an exception without altering it, use a void finalizer with Exception __exception as a parameter. The special __exception parameter will be null if no exception occurred.

public class OriginalCode
{
    public void MightFail()
    {
        throw new Exception("fail");
    }
}

[HarmonyPatch(typeof(OriginalCode), nameof(OriginalCode.MightFail))]
class Patch
{
    static void Finalizer(Exception __exception)
    {
        if (__exception is not null)
            FileLog.Log("caught exception: " + __exception);
    }
}

Changing and rethrowing exceptions

To remap exceptions, return a new exception from the finalizer. This replaces the original exception with a new one.

public class MyException : Exception
{
    public MyException(string message, Exception innerException) : base(message, innerException) { }
}

public class OriginalCode
{
    public void MightFail()
    {
        throw new InvalidOperationException("something went wrong");
    }
}

[HarmonyPatch(typeof(OriginalCode), nameof(OriginalCode.MightFail))]
class Patch
{
    static Exception Finalizer(Exception __exception)
    {
        return __exception is not null ? new MyException("wrapped", __exception) : null;
    }
}

Running cleanup code

Finalizers are ideal for cleanup or resource management logic that must execute regardless of success or failure - similar to a finally block in standard C#.

public class OriginalCode
{
    public static StreamWriter sharedWriter;

    public void WriteData(string data)
    {
        sharedWriter.Write(data);
    }
}

[HarmonyPatch(typeof(OriginalCode), nameof(OriginalCode.WriteData))]
class Patch
{
    static Exception Finalizer(Exception __exception)
    {
        OriginalCode.sharedWriter?.Flush(); // always flush, even if an exception occurred
        return __exception; // rethrow the original exception (if any)
    }
}

Beside their handling of exceptions they can receive the same arguments as Postfixes.

  • Edit this page
In this article
Back to top Generated by DocFX