假设我们有这段代码:
Action<int> gf = k => { };
Action<int> gfa = k => { k++; };
我如何确定gf
没有正文或陈述?
是否可以获得Action
内的语句数量?
类似GetNumberOfStatements(gf)
的内容应该return 0
或者也许HasEmptyBody(gf)应该return true
;
答案 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