如下所示的是一個(gè).NET程序。我們?cè)谶@段程序中定義了一個(gè)作整數(shù)加法運(yùn)算的Add方法,但是我希望將針對(duì)這個(gè)方法的調(diào)用轉(zhuǎn)移到另一個(gè)Add2方法上,為此我定義了一個(gè)Override方法。
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); }
從如下所示的輸出可以看出:雖然源程序我們調(diào)用的是Add方法,實(shí)際上最終的調(diào)用被轉(zhuǎn)移到Add2方法上。
<https://img2018.cnblogs.com/blog/19327/201908/19327-20190814204316432-1731870787.png>
我們知道通過C#編寫的.NET程序在編譯后會(huì)轉(zhuǎn)化成IL
Code,在運(yùn)行時(shí)以及時(shí)編譯的方式轉(zhuǎn)化成機(jī)器指令。如果想“篡改”某個(gè)方法的實(shí)現(xiàn),要么在JIT之前改變IL代碼,要么直接修改最終的機(jī)器指令。Override方法采用的是第二種解決方案,如下所示的該方法的實(shí)現(xiàn),基本的思路就是將將原方法的機(jī)器指令修改為JUMP(對(duì)應(yīng)x86二進(jìn)制為
0xE9)指令實(shí)現(xiàn)向目標(biāo)方法的跳轉(zhuǎn)。
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); }
這個(gè)方式有時(shí)候會(huì)很有用,我最近應(yīng)用的場(chǎng)景是希望篡改.NET
Core應(yīng)用中針對(duì)IHostEnvironment的如下三個(gè)擴(kuò)展方法的實(shí)現(xiàn),因?yàn)槲覀兊牟渴瓠h(huán)境并沒有按照默認(rèn)的命名約定(Development、Staging和Production)這樣導(dǎo)致了這三個(gè)方法返回錯(cuò)誤的結(jié)果。但是IsDevelopment方法的返回結(jié)果在.NET
Core服務(wù)承載系統(tǒng)中很重要,所以不得不篡改它的實(shí)現(xiàn)邏輯。
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"; }
從某種意義上講,這也體現(xiàn)了.NET Core Hosting System在設(shè)計(jì)上的一個(gè)問題,希望在以后的版本中能夠解決這個(gè)問題。
熱門工具 換一換