以下所示的是一個.NET程序。咱們在這段程序中定義了一個做整數加法運算的Add方法,可是我但願將針對這個方法的調用轉移到另外一個Add2方法上,爲此我定義了一個Override方法。ide
class Program { static void Main() { Override(() => Add(default, default), () => Add2(default, default)); Console.WriteLine($"Add(1, 1) == {Add(1, 1)}"); Console.ReadLine(); } public static int Add(int x, int y) => x + y; public static int Add2(int x, int y) => x + y + 1; public static void Override(Expression<Action> originalCall, Expression<Action> targetCall); }
從以下所示的輸出能夠看出:雖然源程序咱們調用的是Add方法,實際上最終的調用被轉移到Add2方法上。this
咱們知道經過C#編寫的.NET程序在編譯後會轉化成IL Code,在運行時以及時編譯的方式轉化成機器指令。若是想「篡改」某個方法的實現,要麼在JIT以前改變IL代碼,要麼直接修改最終的機器指令。Override方法採用的是第二種解決方案,以下所示的該方法的實現,基本的思路就是將將原方法的機器指令修改成JUMP(對應x86二進制爲0xE9)指令實現向目標方法的跳轉。spa
public static void Override(Expression<Action> originalCall, Expression<Action> targetCall) { var originalMethod = ((MethodCallExpression)originalCall.Body).Method; var targetMethod = ((MethodCallExpression)targetCall.Body).Method; RuntimeHelpers.PrepareMethod(originalMethod.MethodHandle); RuntimeHelpers.PrepareMethod(targetMethod.MethodHandle); var sourceAddress = originalMethod.MethodHandle.GetFunctionPointer(); var targetAddress = (long)targetMethod.MethodHandle.GetFunctionPointer(); int offset = (int)(targetAddress - (long)sourceAddress - 4 - 1); byte[] instruction = { 0xE9, // JUMP (byte)(offset & 0xFF), (byte)((offset >> 8) & 0xFF), (byte)((offset >> 16) & 0xFF), (byte)((offset >> 24) & 0xFF) }; Marshal.Copy(instruction, 0, sourceAddress, instruction.Length); }
這個方式有時候會頗有用,我最近應用的場景是但願篡改.NET Core應用中針對IHostEnvironment的以下三個擴展方法的實現,由於咱們的部署環境並無按照默認的命名約定(Development、Staging和Production)這樣致使了這三個方法返回錯誤的結果。可是IsDevelopment方法的返回結果在.NET Core服務承載系統中很重要,因此不得不篡改它的實現邏輯。設計
public static class HostEnvironmentEnvExtensions { public static bool IsDevelopment(this IHostEnvironment hostEnvironment) =>hostEnvironment.IsEnvironment(Environments.Development); public static bool IsProduction(this IHostEnvironment hostEnvironment) =>hostEnvironment.IsEnvironment(Environments.Production); public static bool IsStaging(this IHostEnvironment hostEnvironment) =>hostEnvironment.IsEnvironment(Environments.Staging); } public static class Environments { public static readonly string Development = "Development"; public static readonly string Production = "Production"; public static readonly string Staging = "Staging"; }
從某種意義上講,這也體現了.NET Core Hosting System在設計上的一個問題,但願在之後的版本中可以解決這個問題。code