Хочу поделиться, как я решил эту проблему.
Я написал целую библиотеку с большим количеством функций, таких как события, которые вызываются на съемочной площадке или случаются. Но для ответа я хочу разбить его на простейший пример.
void Main()
{
MethodInfo targetMethod = typeof(TargetObj).GetProperty("Count").GetSetMethod();
MethodInfo injectMethod = typeof(InjectObj).GetMethod("SetCount");
targetMethod.InjectMethod(injectMethod);
var targetInstance = new TargetObj();
targetInstance.Count = 3;
}
public class TargetObj
{
public int Count { get; set; }
}
public class InjectObj
{
public void SetCount(int value)
{
GetBackingField(this, "Count").SetValue(this, value);
Console.WriteLine($"Count has been set to [{value}]");
}
}
public static FieldInfo GetBackingField(object obj, string propertyName) => obj.GetType().GetField($"<{propertyName}>k__BackingField", BindingFlags.Instance | BindingFlags.NonPublic);
Если этот код запущен, вывод консоли будет:
Счет был установлен на [3]
Возможности этого подхода безграничны, но я хотел бы кратко взглянуть на то, как мне удалось этого достичь.
Обратите внимание, что я сделал этот пример, используя Linqpad.
Я не хочу без необходимости удлинять пост, но мне нужно включить метод расширения MethodInfo "InjectMethod" для полного понимания происходящего.
public static class MethodInfoExtensions
{
public static void InjectMethod(this MethodInfo target, MethodInfo inject)
{
RuntimeHelpers.PrepareMethod(inject.MethodHandle);
RuntimeHelpers.PrepareMethod(target.MethodHandle);
unsafe
{
if (IntPtr.Size == 4)
{
int* inj = (int*)inject.MethodHandle.Value.ToPointer() + 2;
int* tar = (int*)target.MethodHandle.Value.ToPointer() + 2;
if (Debugger.IsAttached)
{
byte* injInst = (byte*)*inj;
byte* tarInst = (byte*)*tar;
int* injSrc = (int*)(injInst + 1);
int* tarSrc = (int*)(tarInst + 1);
*tarSrc = (((int)injInst + 5) + *injSrc) - ((int)tarInst + 5);
}
else
{
*tar = *inj;
}
}
else
{
long* inj = (long*)inject.MethodHandle.Value.ToPointer() + 1;
long* tar = (long*)target.MethodHandle.Value.ToPointer() + 1;
if (Debugger.IsAttached)
{
byte* injInst = (byte*)*inj;
byte* tarInst = (byte*)*tar;
int* injSrc = (int*)(injInst + 1);
int* tarSrc = (int*)(tarInst + 1);
*tarSrc = (((int)injInst + 5) + *injSrc) - ((int)tarInst + 5);
}
else
{
*tar = *inj;
}
}
}
}
}
Используя этот вид замены метода, я бы порекомендовал только для целей тестирования или, возможно, в качестве своего рода инструмента отладки.
Я уверен, что реализация этого в реальном выпуске программного обеспечения вызовет больше проблем, чем решит.