Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/328.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 替换方法中的指令';方法体_C#_Reflection_Reflection.emit_Cil_Mono.cecil - Fatal编程技术网

C# 替换方法中的指令';方法体

C# 替换方法中的指令';方法体,c#,reflection,reflection.emit,cil,mono.cecil,C#,Reflection,Reflection.emit,Cil,Mono.cecil,(首先,这是一篇很长的帖子,但别担心:我已经实现了所有功能,我只是想征求您的意见,或者其他可能的选择。) 我无法实现以下内容:;我非常感谢您的帮助: 我得到一个类型作为参数 我使用反射定义了一个子类。请注意,我不打算修改原始类型,而是创建一个新类型 我为原始类的每个字段创建一个属性,如下所示: public class OriginalClass { private int x; } public class Subclass : OriginalClass { privat

(首先,这是一篇很长的帖子,但别担心:我已经实现了所有功能,我只是想征求您的意见,或者其他可能的选择。)

我无法实现以下内容:;我非常感谢您的帮助:

  • 我得到一个
    类型
    作为参数
  • 我使用反射定义了一个子类。请注意,我不打算修改原始类型,而是创建一个新类型
  • 我为原始类的每个字段创建一个属性,如下所示:

    public class OriginalClass {
        private int x;
    }
    
    
    public class Subclass : OriginalClass {
        private int x;
    
        public int X {
            get { return x; }
            set { x = value; }
        }
    
    }
    
  • 对于超类的每个方法,我在子类中创建了一个类似的方法。该方法的主体必须相同,只是我将指令
    ldfld x
    替换为
    callvirt this.get\u x
    ,也就是说,我调用get访问器,而不是直接从字段中读取

  • 我在步骤4中遇到了问题。我知道你不应该像这样操纵代码,但我真的需要

    以下是我尝试过的:

    尝试#1:使用Mono.Cecil。这将允许我将方法体解析为人类可读的
    指令
    ,并轻松替换指令。但是,原始类型不在.dll文件中,因此我找不到用Mono.Cecil加载它的方法。将类型写入.dll,然后加载它,然后修改它并将新类型写入磁盘(我认为这是使用Mono.Cecil创建类型的方式),然后加载它似乎是一个巨大的开销

    尝试#2:使用Mono.Reflection。这也允许我将正文解析为
    指令
    ,但是我不支持替换指令。我已经使用Mono.Reflection实现了一个非常丑陋且效率低下的解决方案,但是它还不支持包含try-catch语句的方法(尽管我想我可以实现它),而且我担心它可能会在其他情况下不起作用,因为我使用的是
    ILGenerator
    ,方式有些不寻常。而且,它非常难看;)。以下是我所做的:

    private void TransformMethod(MethodInfo methodInfo) {
    
        // Create a method with the same signature.
        ParameterInfo[] paramList = methodInfo.GetParameters();
        Type[] args = new Type[paramList.Length];
        for (int i = 0; i < args.Length; i++) {
            args[i] = paramList[i].ParameterType;
        }
        MethodBuilder methodBuilder = typeBuilder.DefineMethod(
            methodInfo.Name, methodInfo.Attributes, methodInfo.ReturnType, args);
        ILGenerator ilGen = methodBuilder.GetILGenerator();
    
        // Declare the same local variables as in the original method.
        IList<LocalVariableInfo> locals = methodInfo.GetMethodBody().LocalVariables;
        foreach (LocalVariableInfo local in locals) {
            ilGen.DeclareLocal(local.LocalType);
        }
    
        // Get readable instructions.
        IList<Instruction> instructions = methodInfo.GetInstructions();
    
        // I first need to define labels for every instruction in case I
        // later find a jump to that instruction. Once the instruction has
        // been emitted I cannot label it, so I'll need to do it in advance.
        // Since I'm doing a first pass on the method's body anyway, I could
        // instead just create labels where they are truly needed, but for
        // now I'm using this quick fix.
        Dictionary<int, Label> labels = new Dictionary<int, Label>();
        foreach (Instruction instr in instructions) {
            labels[instr.Offset] = ilGen.DefineLabel();
        }
    
        foreach (Instruction instr in instructions) {
    
            // Mark this instruction with a label, in case there's a branch
            // instruction that jumps here.
            ilGen.MarkLabel(labels[instr.Offset]);
    
            // If this is the instruction that I want to replace (ldfld x)...
            if (instr.OpCode == OpCodes.Ldfld) {
                // ...get the get accessor for the accessed field (get_X())
                // (I have the accessors in a dictionary; this isn't relevant),
                MethodInfo safeReadAccessor = dataMembersSafeAccessors[((FieldInfo) instr.Operand).Name][0];
                // ...instead of emitting the original instruction (ldfld x),
                // emit a call to the get accessor,
                ilGen.Emit(OpCodes.Callvirt, safeReadAccessor);
    
            // Else (it's any other instruction), reemit the instruction, unaltered.
            } else {
                Reemit(instr, ilGen, labels);
            }
    
        }
    
    }
    
    分支指令是一种特殊情况,因为
    instr.Operand
    SByte
    ,但
    Emit
    需要类型为
    Label
    的操作数。因此需要
    字典标签

    正如你所看到的,这是非常可怕的。更重要的是,它并不适用于所有情况,例如,对于包含try-catch语句的方法,因为我没有使用
    ILGenerator
    的方法
    beginceptionblock
    BeginCatchBlock
    等发出它们。这越来越复杂了。我想我可以做到:
    MethodBody
    有一个
    ExceptionHandlingClause
    列表,其中应该包含执行此操作所需的信息。但我不喜欢这个解决方案,所以我会把它作为最后的解决方案

    尝试#3:返回并复制MethodBody.GetILAsByteArray()返回的字节数组,因为我只想用一条指令替换另一条大小相同的指令,该指令产生完全相同的结果:它在堆栈上加载相同类型的对象,等,所以不会有任何标签转移,一切都应该完全一样的工作。我已经这样做了,替换了数组的特定字节,然后调用
    MethodBuilder.CreateMethodBody(byte[],int)
    ,但是我仍然会得到相同的错误和异常,我仍然需要声明局部变量,否则我会得到一个错误。。。即使我只是简单地复制方法的主体,并且不做任何更改。 因此,这是更有效的,但我仍然要处理例外情况,等等

    以下是尝试#3的实现,以防有人感兴趣:

    private void TransformMethod(MethodInfo methodInfo, Dictionary<string, MethodInfo[]> dataMembersSafeAccessors, ModuleBuilder moduleBuilder) {
    
        ParameterInfo[] paramList = methodInfo.GetParameters();
        Type[] args = new Type[paramList.Length];
        for (int i = 0; i < args.Length; i++) {
            args[i] = paramList[i].ParameterType;
        }
        MethodBuilder methodBuilder = typeBuilder.DefineMethod(
            methodInfo.Name, methodInfo.Attributes, methodInfo.ReturnType, args);
    
        ILGenerator ilGen = methodBuilder.GetILGenerator();
    
        IList<LocalVariableInfo> locals = methodInfo.GetMethodBody().LocalVariables;
        foreach (LocalVariableInfo local in locals) {
            ilGen.DeclareLocal(local.LocalType);
        }
    
        byte[] rawInstructions = methodInfo.GetMethodBody().GetILAsByteArray();
        IList<Instruction> instructions = methodInfo.GetInstructions();
    
        int k = 0;
        foreach (Instruction instr in instructions) {
    
            if (instr.OpCode == OpCodes.Ldfld) {
    
                MethodInfo safeReadAccessor = dataMembersSafeAccessors[((FieldInfo) instr.Operand).Name][0];
    
                // Copy the opcode: Callvirt.
                byte[] bytes = toByteArray(OpCodes.Callvirt.Value);
                for (int m = 0; m < OpCodes.Callvirt.Size; m++) {
                    rawInstructions[k++] = bytes[put.Length - 1 - m];
                }
    
                // Copy the operand: the accessor's metadata token.
                bytes = toByteArray(moduleBuilder.GetMethodToken(safeReadAccessor).Token);
                for (int m = instr.Size - OpCodes.Ldfld.Size - 1; m >= 0; m--) {
                    rawInstructions[k++] = bytes[m];
                }
    
            // Skip this instruction (do not replace it).
            } else {
                k += instr.Size;
            }
    
        }
    
        methodBuilder.CreateMethodBody(rawInstructions, rawInstructions.Length);
    
    }
    
    
    private static byte[] toByteArray(int intValue) {
        byte[] intBytes = BitConverter.GetBytes(intValue);
        if (BitConverter.IsLittleEndian)
            Array.Reverse(intBytes);
        return intBytes;
    }
    
    
    
    private static byte[] toByteArray(short shortValue) {
        byte[] intBytes = BitConverter.GetBytes(shortValue);
        if (BitConverter.IsLittleEndian)
            Array.Reverse(intBytes);
        return intBytes;
    }
    
    private void TransformMethod(MethodInfo MethodInfo、字典数据成员safeaccessors、ModuleBuilder ModuleBuilder){
    ParameterInfo[]paramList=methodInfo.GetParameters();
    Type[]args=新类型[paramList.Length];
    for(int i=0;i=0;m--){
    raw指令[k++]=字节[m];
    }
    //跳过此说明(不要替换它)。
    }否则{
    k+=仪表尺寸;
    }
    }
    methodBuilder.CreateMethodBody(rawInstructions,rawInstructions.Length);
    }
    专用静态字节[]toByteArray(int intValue){
    byte[]intBytes=BitConverter.GetBytes(intValue);
    if(位转换器.IsLittleEndian)
    Array.Reverse(intBytes);
    返回整数字节;
    }
    专用静态字节[]toByteArray(短值){
    byte[]intBytes=BitConverter.GetBytes(shortValue);
    if(位转换器.IsLittleEndian)
    Array.Reverse(intBytes);
    返回整数字节;
    }
    
    (我知道它不好看。对不起,我很快就把它拼在一起,看看是否能用。)

    <
    private void TransformMethod(MethodInfo methodInfo, Dictionary<string, MethodInfo[]> dataMembersSafeAccessors, ModuleBuilder moduleBuilder) {
    
        ParameterInfo[] paramList = methodInfo.GetParameters();
        Type[] args = new Type[paramList.Length];
        for (int i = 0; i < args.Length; i++) {
            args[i] = paramList[i].ParameterType;
        }
        MethodBuilder methodBuilder = typeBuilder.DefineMethod(
            methodInfo.Name, methodInfo.Attributes, methodInfo.ReturnType, args);
    
        ILGenerator ilGen = methodBuilder.GetILGenerator();
    
        IList<LocalVariableInfo> locals = methodInfo.GetMethodBody().LocalVariables;
        foreach (LocalVariableInfo local in locals) {
            ilGen.DeclareLocal(local.LocalType);
        }
    
        byte[] rawInstructions = methodInfo.GetMethodBody().GetILAsByteArray();
        IList<Instruction> instructions = methodInfo.GetInstructions();
    
        int k = 0;
        foreach (Instruction instr in instructions) {
    
            if (instr.OpCode == OpCodes.Ldfld) {
    
                MethodInfo safeReadAccessor = dataMembersSafeAccessors[((FieldInfo) instr.Operand).Name][0];
    
                // Copy the opcode: Callvirt.
                byte[] bytes = toByteArray(OpCodes.Callvirt.Value);
                for (int m = 0; m < OpCodes.Callvirt.Size; m++) {
                    rawInstructions[k++] = bytes[put.Length - 1 - m];
                }
    
                // Copy the operand: the accessor's metadata token.
                bytes = toByteArray(moduleBuilder.GetMethodToken(safeReadAccessor).Token);
                for (int m = instr.Size - OpCodes.Ldfld.Size - 1; m >= 0; m--) {
                    rawInstructions[k++] = bytes[m];
                }
    
            // Skip this instruction (do not replace it).
            } else {
                k += instr.Size;
            }
    
        }
    
        methodBuilder.CreateMethodBody(rawInstructions, rawInstructions.Length);
    
    }
    
    
    private static byte[] toByteArray(int intValue) {
        byte[] intBytes = BitConverter.GetBytes(intValue);
        if (BitConverter.IsLittleEndian)
            Array.Reverse(intBytes);
        return intBytes;
    }
    
    
    
    private static byte[] toByteArray(short shortValue) {
        byte[] intBytes = BitConverter.GetBytes(shortValue);
        if (BitConverter.IsLittleEndian)
            Array.Reverse(intBytes);
        return intBytes;
    }
    
    byte[] code = body.GetILAsByteArray();
    ILReader reader = new ILReader(method);
    ILInfoGetTokenVisitor visitor = new ILInfoGetTokenVisitor(ilInfo, code);
    reader.Accept(visitor);
    
    ilInfo.SetCode(code, body.MaxStackSize);