public abstract class QSingleton<T> where T : QSingleton<T>, new() {
public static T instance = null; protected QSingleton() { }
public static T Instance { get {
Console.WriteLine($"{typeof(T).Name}.Instance: impl=QSingleton");
public class SampleA : QSingleton<SampleA> {
public SampleA() { Console.WriteLine("SampleA ctor"); }
public class SampleB : QSingleton<SampleB> {
public SampleB() { Console.WriteLine("SampleB ctor"); }
public class TypeHelper<T> where T : PatchTarget.QSingleton<T>, new() {
public static T InstanceHack() {
var instance = AccessTools.Field(typeof(T), "instance").GetValue(null) as T;
Console.WriteLine($"{typeof(T).Name}.Instance: impl=InstanceHack, instance={instance?.ToString() ?? "null"}");
public static class HarmonyPatch {
public static Harmony harmony = new Harmony("Try");
public static void init() {
var miOriginal = AccessTools.Property(typeof(PatchTarget.QSingleton<PatchTarget.SampleB>), "Instance").GetMethod;
var miHack = AccessTools.Method(typeof(TypeHelper<PatchTarget.SampleB>), "InstanceHackPrefix");
harmony.Patch(miOriginal, prefix: new HarmonyMethod(miHack));
public static class MonoModPatch {
public static MonoMod.RuntimeDetour.Detour sHook;
public static void init() {
var miOriginal = AccessTools.Property(typeof(PatchTarget.QSingleton<PatchTarget.SampleB>), "Instance").GetMethod;
var miHack = AccessTools.Method(typeof(TypeHelper<PatchTarget.SampleB>), "InstanceHack");
sHook = new MonoMod.RuntimeDetour.Detour(miOriginal, miHack);
public static void Main() {
_ = PatchTarget.SampleA.Instance;
try { Patch.MonoModPatch.init(); } catch (Exception e) { Console.WriteLine($"MonoMod error: {e.ToString()}"); }
_ = PatchTarget.SampleB.Instance;
_ = PatchTarget.SampleA.Instance;
Console.WriteLine($"done");