使用for循环和条件语句定义方法

时间:2014-02-07 13:50:40

标签: c# reflection reflection.emit ikvm

我必须使用Reflection.Emit来定义一个相当复杂的方法,因为我必须在一个字段上执行for循环并且具有break和return的条件。我想用反射重新创建的方法在常规代码中看起来像这样:

override int GetKeyImpl(Type obj0)
{
    int answer = -1;
    for(int i = 0; i < knownTypes.length; i++){
          if(knowntypes[i] == obj0){
                answer = i;
                break;
          }
    }
    return answer;
} 

我解决这个问题的想法是生成一个带有反射的方法,该方法将调用重定向到我的原始方法并返回int

我需要知道如何执行for循环并使用OpCodes中断以在对类内部的数组进行条件检查时重新创建方法。我已经搜索过教程,但没有找到比添加两个整数更进一步的内容。

编辑:忘记提及它,我使用 IKVM.Reflection ,knownTypes是Type []的数组。我写的方法是覆盖抽象的方法。

2 个答案:

答案 0 :(得分:4)

这应该重现你指定的方法:

TypeBuilder type = /* ... */;
FieldInfo knownFields = /* ... */;

// Finding dependencies via reflection
var baseMethod = type.BaseType.GetMethod(
    "GetKeyImpl",
    BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);

var typeEqualsOperator = typeof(Type).GetMethod(
    "op_Equality",
    BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic,
    null,
    new[] { typeof(Type), typeof(Type) },
    null);

// Declaring the method
var getKeyImpl = type.DefineMethod(
    baseMethod.Name,
    baseMethod.Attributes & ~(MethodAttributes.Abstract |
                              MethodAttributes.NewSlot));

// Setting return type
getKeyImpl.SetReturnType(typeof(int));

// Adding parameters
getKeyImpl.SetParameters(typeof(Type));
getKeyImpl.DefineParameter(1, ParameterAttributes.None, "obj0");

// Override the base method
type.DefineMethodOverride(getKeyImpl, baseMethod);

var il = getKeyImpl.GetILGenerator();

// Preparing locals
var answer = il.DeclareLocal(typeof(int));
var i = il.DeclareLocal(typeof(int));

// Preparing labels
var loopCondition = il.DefineLabel();
var loopIterator = il.DefineLabel();
var returnLabel = il.DefineLabel();
var loopBody = il.DefineLabel();

// Writing body

// answer = -1
il.Emit(OpCodes.Ldc_I4_M1);
il.Emit(OpCodes.Stloc, answer);

// i = 0
il.Emit(OpCodes.Ldc_I4_0);
il.Emit(OpCodes.Stloc, i);

// jump to loop condition
il.Emit(OpCodes.Br_S, loopCondition);

// begin loop body
il.MarkLabel(loopBody);

// if (obj0 != knownTypes[i]) continue
il.Emit(OpCodes.Ldarg_0); // omit if 'knownTypes' is static
il.Emit(OpCodes.Ldfld, knownTypes); // use 'Ldsfld' if 'knownTypes' is static
il.Emit(OpCodes.Ldloc, i);
il.Emit(OpCodes.Ldelem_Ref);
il.Emit(OpCodes.Ldarg_1); // use 'Ldarg_0' if 'knownTypes' is static
il.Emit(OpCodes.Call, typeEqualsOperator);
il.Emit(OpCodes.Brfalse_S, loopIterator);

// answer = i; jump to return
il.Emit(OpCodes.Ldloc, i);
il.Emit(OpCodes.Stloc, answer);
il.Emit(OpCodes.Br_S, returnLabel);

// begin loop iterator
il.MarkLabel(loopIterator);

// i = i + 1
il.Emit(OpCodes.Ldloc, i);
il.Emit(OpCodes.Ldc_I4_1);
il.Emit(OpCodes.Add);
il.Emit(OpCodes.Stloc, i);

// begin loop condition
il.MarkLabel(loopCondition);

// if (i < knownTypes.Length) jump to loop body
il.Emit(OpCodes.Ldloc, i);
il.Emit(OpCodes.Ldarg_0); // omit if 'knownTypes' is static
il.Emit(OpCodes.Ldfld, knownTypes); // use 'Ldsfld' if 'knownTypes' is static
il.Emit(OpCodes.Ldlen);
il.Emit(OpCodes.Conv_I4);
il.Emit(OpCodes.Blt_S, loopBody);

// return answer
il.MarkLabel(returnLabel);
il.Emit(OpCodes.Ldloc, answer);
il.Emit(OpCodes.Ret);

// Finished!

反编译结果如预期:

override int GetKeyImpl(Type obj0)
{
    for (int i = 0; i < this.knownTypes.Length; i++)
    {
        if (this.knownTypes[i] == obj0)
            return i;
    }
    return -1;
}

如果您有权访问.NET Reflector,那么您可能感兴趣Reflection.Emit Language Add-In。或者,用C#代码编写原型,然后通过反汇编程序运行它以查看原始IL。

如果可以制作方法static(并接受knownTypes作为参数或使其成为static字段),那么您可以使用LINQ编写方法体表达树。不幸的是,你不能使用这种技术组合实例方法体;他们必须是static。例如:

var method = typeBuilder.DefineMethod(
    "GetKeyImpl",
    MethodAttributes.Private |
    MethodAttributes.Static | 
    MethodAttributes.HideBySig);

var type = E.Parameter(typeof(Type), "type");
var knownTypes = E.Parameter(typeof(Type[]), "knownTypes");

var answer = E.Variable(typeof(int), "answer");
var i = E.Variable(typeof(int), "i");

var breakTarget = E.Label("breakTarget");
var continueTarget = E.Label("continueTarget");
var returnTarget = E.Label(typeof(int), "returnTarget");

var forLoop = E.Block(
    new[] { i },
    E.Assign(i, E.Constant(0)),
    E.Loop(
        E.Block(
            E.IfThen(
                E.GreaterThanOrEqual(i, E.ArrayLength(knownTypes)),
                E.Break(breakTarget)),
            E.IfThen(
                E.Equal(E.ArrayIndex(knownTypes, i), type),
                E.Return(returnTarget, i)),
            E.Label(continueTarget),
            E.PreIncrementAssign(i))),
    E.Label(breakTarget));

var body = E.Lambda<Func<Type, Type[], int>>(
    E.Block(
        new[] { answer },
        E.Assign(answer, E.Constant(-1)),
        forLoop,
        E.Label(returnTarget, answer)),
    type,
    knownTypes);

body.CompileToMethod(method);

return method;

上面的示例接受knownTypes作为第二个参数。重构从静态字段读取将是直截了当的。反编译结果再次符合预期:

private static int GetKeyImpl(Type type, Type[] knownTypes)
{
    for (int i = 0; i < knownTypes.Length; i++)
    {
        if (knownTypes[i] == type)
            return i;
    }
    return -1;
}

答案 1 :(得分:0)

计算如何为方法生成IL的简单方法是创建一个包含方法的简单控制台应用程序。然后构建它并对其运行ILDasm以查看必要的IL指令弥补方法。

一旦你看到这些说明,编写代码以发出必要的OpCodes就不会太难了。