Заменить метод установки свойства Ptr

public delegate void SetProp(object obj);

void Main()
{
    TestObj obj = new TestObj();

    SetProp setPropDel = (SetProp)SetProp.CreateDelegate(typeof(SetProp), 
    obj, obj.GetSetPropDelegate().GetMethodInfo());
    MethodInfo setPropMethod = setPropDel.GetMethodInfo();

    ///Replacing Count Set-Method Pointer with new Method
    typeof(TestObj).GetProperty("Count").GetSetMethod().ReplaceMethodPtr(setPropMethod);
    obj.Count = 1;

}

public class TestObj
{
    public SetProp GetSetPropDelegate() => SetPropValue;

    private void SetPropValue(object obj) => Console.WriteLine(obj); ///<--- NullReferenceException obj is null.

    public int Count { get; set; }

}

Привет, я пытался заменить частный set-Method для автоматического свойства «Count» в моем классе TestObj.

Сама замена работает. Потому что, когда я использую установщик, например obj.Count = 1, вызывается новый метод.

Мой вопрос: можно ли передать параметр в этот новый метод? По крайней мере, мне нужно новое значение, которое выделяется свойству Count-Property. В этом случае: 1.

Моя цель — сделать возможным замену Set-методов автоматических свойств для создания события при вызове нового Set-метода, а также сохранить базовую функцию Set-метода.

Надеюсь, понятно, чего я хочу добиться. Возможно, я столкнулся с чем-то невозможным здесь, но я уверен, что многие люди гораздо лучше понимают, что происходит.

Спасибо за чтение.


person Jiji    schedule 05.09.2018    source источник


Ответы (1)


Хочу поделиться как я решил эту проблему.

Я написал целую библиотеку с большим количеством функций, таких как события, которые вызываются на съемках или по особым случаям. Но для ответа я хочу разбить его на самый простой пример.

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);

Если этот код запущен, консольный вывод будет следующим: Count был установлен на [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;
                }
            }
        }
    }
}

Использование такого рода замены метода я бы рекомендовал только для целей тестирования или, возможно, в качестве своего рода инструмента отладки. Я почти уверен, что реализация этого в реальном выпуске программного обеспечения создаст больше проблем, чем решит.

person Jiji    schedule 08.09.2018