如何使用 OpCodes 和 Emit 将参数设置为类型 T 的实例化属性?

问题描述 投票:0回答:2

我正在尝试编写一个返回 DynamicMethod 的方法,该方法在调用时将第一个参数设置为类型 T 的对象,该对象称为 Value 并在类实例化时设置,这是我正确的代码现在:

using HarmonyLib;
using System.Reflection;
using System.Reflection.Emit;

public class OriginalCode<T>
{
public delegate void PostFix(ref T __result);
    public T Value { get; set; }
    public OriginalCode(T value)
    {
        Value = value;
    }

    public MethodInfo Postfix()
    {
        var result = new DynamicMethod("NewPostfix", typeof(void), new Type[] { typeof(T).MakeByRefType() }, typeof(Program).Module);
        result.DefineParameter(1, ParameterAttributes.None, "__result");
        
        ILGenerator il = result.GetILGenerator();
        
        Type type = typeof(OriginalCode<T>);
        PropertyInfo propertyInfo = type.GetProperty("Value");

        // create a reference to the Value Property
        PropertyInfo ValueProperty = typeof(OriginalCode<T>).GetProperty("Value");

        il.Emit(OpCodes.Ldarg_0);
        il.Emit(OpCodes.Ldstr, Value);
        il.Emit(OpCodes.Stind_Ref);
        il.Emit(OpCodes.Ret);
        return result;
    }
}

所以我会这样使用它:


string testRefStr = "Test";
var originalCodeInstance = new OriginalCode<string>("New Test");
var newStringDelegate = (OriginalCode<string>.PostFix)originalCodeInstance.Postfix().CreateDelegate(typeof(OriginalCode<string>.PostFix));
newStringDelegate(ref testRefStr);

int testRefInt = 55;
var originalCodeInstance = new OriginalCode<int>(75);
var newIntDelegate = (OriginalCode<int>.PostFix)originalCodeInstance.Postfix().CreateDelegate(typeof(OriginalCode<int>.PostFix));
newIntDelegate(ref testRefInt);

这目前仅在 T 是字符串时才有效,但我需要它适用于所有类型,包括引用和原始类型以及任务/接口和其他抽象类型。

我将如何着手实施这个?我不是很擅长 Opcodes 和 IL 它在我的知识范围之外 - 我真的只是在这部分使用它,我非常接近但我很困。 谢谢大家

我知道其他类型失败的原因是

Opcodes.Ldstr
只加载一个字符串 - 我也尝试过
OpCodes.Ldobj
但它对我不起作用 - 因为我无法在 DynamicMethod 中调试 OpCodes 我总是得到无用的输出,例如
Invalid Code detected
.

我曾尝试使用

OpCodes.Callvirt
但由于与上述类似的原因也没有用

我试图获取

Value
属性的 Getter 的方法定义并尝试使用
Opcodes.Call
- 这也没有用。

如果您认为这些操作码应该有效,请记住我不熟悉操作码并且很可能没有正确使用它哈哈 - 请告诉我如何正确使用操作码。

谢谢大家

c# reflection delegates opcode
2个回答
1
投票

让我们看看你想要什么实现

int testRefInt = 55;
new OriginalCode<int>(75).Postfix().invoke(OriginalCode, new object[] {testRefInt})

//testRefString should now be "NewString" and testRefInt should now be 75

你要求的是不可能的。表达式

new object[] {testRefInt}
没有引用
testRefInt
;它将 testRefInt (55) 的
current value
复制到堆栈上,“框”,然后将对新构造的框的引用传递到数组中。无论您的代码做什么它都不会影响
testRefInt
。那不是你能做的事。

要做到 that,您需要在某处传递

ref testRefInt
,但您不能“装箱”一个
ref
ref
值仅在本地、堆栈上或作为
ref struct
值上的字段有效,其中 also 仅在本地、堆栈或其他
ref struct
值的字段中有效)。


0
投票

经过讨论,这里的基本要求似乎是将 arg 设置为任意值,从(出于某种原因)委托到静态方法。现在,这里的问题是您可以在 IL 中直接编写 的类型非常有限——整数、字符串、浮点数、空值等。如果您只需要处理一些非常具体的类型,那么您可以特别- 每种情况,但在一般情况下:那是行不通的。因此,典型的解决方法是使用 ref-emit 声明一个动态的 type,带有一个静态字段,我们可以在其中推送值,然后基本上使用(作为我们的方法主体):

larga {the arg}
ldsfld {the field with the value we want}
stobj {the type of the field}
ret
© www.soinside.com 2019 - 2024. All rights reserved.