4

假设我们有这段代码:

Action<int> gf = k => { };
Action<int> gfa = k => { k++; };

我如何确定gf没有正文或语句?
是否可以在 a 中获取语句数量Action

类似于GetNumberOfStatements(gf)which should的东西return 0
或者也许 HasEmptyBody(gf) 应该return true

4

3 回答 3

4

好吧,这有点不确定,但是您可以检查方法体的 IL 并检查它是否为空或完全由Nops组成(当然除了最后的 Ret )。

显然,如果编写该方法的编程语言的编译器已编译掉操作而没有任何影响,那么您将在这里得到误报。但我假设您主要对(arg1, arg2, ... ) => { }C# 案例感兴趣,这应该可以正常工作。

public static bool IsEmpty(this Delegate del)
{
    // Null arg-checking omitted.

    short nop = System.Reflection.Emit.OpCodes.Nop.Value;

    var ilArray = del.Method.GetMethodBody().GetILAsByteArray();

    return ilArray.Take(ilArray.Length - 1).All(b => b == nop);
}
于 2013-04-04T14:21:42.370 回答
1

.Net 中的委托只不过是函数指针。就像您无法判断 .Net 方法中有多少 C# 语句一样,您也无法判断 .Net 委托中有多少条语句。部分原因是该方法不一定用 C# 或任何其他具有语句概念的语言编码。它可以直接用基于操作码的 IL 编写

于 2013-04-04T14:18:06.210 回答
0

编辑:决定在我等待构建服务器重新启动时稍微修改一下......

实际上,我的“实用程序库”周围有一些东西可能会对您有所帮助-自然,这里有很大的改进空间-这绝不是用于“生产用途”的:

public static void DumpMethod(Delegate method)
{
    var mb = method.Method.GetMethodBody();
    var il = mb.GetILAsByteArray();
    var opCodes = typeof(System.Reflection.Emit.OpCodes)
        .GetFields()
        .Select(fi => (System.Reflection.Emit.OpCode)fi.GetValue(null));
    var mappedIL = il.Select(op => opCodes.FirstOrDefault(opCode => opCode.Value == op));
    var ilWalker = mappedIL.GetEnumerator();
    while(ilWalker.MoveNext())
    {
        var mappedOp = ilWalker.Current;
        if(mappedOp.OperandType != System.Reflection.Emit.OperandType.InlineNone)
        {
            var byteCount = 0;
            long operand = 0;
            switch(mappedOp.OperandType)
            {
                case System.Reflection.Emit.OperandType.InlineI:
                case System.Reflection.Emit.OperandType.InlineBrTarget:
                case System.Reflection.Emit.OperandType.InlineMethod:                
                case System.Reflection.Emit.OperandType.InlineField:
                case System.Reflection.Emit.OperandType.InlineSig:
                case System.Reflection.Emit.OperandType.InlineString:
                case System.Reflection.Emit.OperandType.InlineType:
                case System.Reflection.Emit.OperandType.InlineSwitch:
                    byteCount = 4;
                    break;
                case System.Reflection.Emit.OperandType.InlineI8:
                case System.Reflection.Emit.OperandType.InlineR:
                    byteCount = 8;
                    break;
            }
            for(int i=0; i < byteCount; i++)
            {
                ilWalker.MoveNext();
                operand |= ((long)ilWalker.Current.Value) << (8 * i);
            }
            Console.WriteLine("{0} {1}", mappedOp.Name, operand);                    
        }
        else
        {
            Console.WriteLine("{0}", mappedOp.Name);
        }
    }
}

试验台:

Func<int,int> addOne = i => i + 1;
DumpMethod(addOne);
Console.WriteLine();
Func<int,string> stuff = i =>
{
    var m = 10312;      
    var j = i + m;
    var k = j * j + i;
    var asStr = k.ToString();
    return asStr;
};
DumpMethod(stuff);

输出:

ldarg.0
ldc.i4.1
add
ret

ldc.i4 10312
stloc.0
ldarg.0
ldloc.0
add
stloc.1
ldloc.1
ldloc.1
mul
ldarg.0
add
stloc.2
ldloca.s 0
ldarg.0
call 167772167
stloc.3
ldloc.3
ret
于 2013-04-04T14:54:59.897 回答