是否可以获取操作中的语句数量或确定它是否有空体?

时间:2013-04-04 14:06:42

标签: c# .net vb.net action func

假设我们有这段代码:

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

我如何确定gf没有正文或陈述? 是否可以获得Action内的语句数量?

类似GetNumberOfStatements(gf)的内容应该return 0 或者也许HasEmptyBody(gf)应该return true;

3 个答案:

答案 0 :(得分: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);
}

答案 1 :(得分:1)

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

答案 2 :(得分: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