比方說,我們有這樣的代碼:是否可以獲取某個操作中的語句數量或確定它是否爲空的主體?
Action<int> gf = k => { };
Action<int> gfa = k => { k++; };
我怎麼能確定gf
有沒有身體或聲明?
是否有可能得到一個Action
內的語句數?
類似於GetNumberOfStatements(gf)
其中應該是return 0
。
或者也許HasEmptyBody(gf)應該return true
;
比方說,我們有這樣的代碼:是否可以獲取某個操作中的語句數量或確定它是否爲空的主體?
Action<int> gf = k => { };
Action<int> gfa = k => { k++; };
我怎麼能確定gf
有沒有身體或聲明?
是否有可能得到一個Action
內的語句數?
類似於GetNumberOfStatements(gf)
其中應該是return 0
。
或者也許HasEmptyBody(gf)應該return true
;
嗯,這有點不妥,但是你可以檢查方法體的IL,並檢查它是否爲空或者完全由Nops組成(除了Ret在課程結束時)。
顯然,如果編寫該方法的編程語言的編譯器已經編譯完成了沒有任何影響的操作,那麼這裏會出現誤報。但我認爲你主要對(arg1, arg2, ...) => { }
C#case感興趣,爲此,它應該可以正常工作。
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);
}
根據vcsjones你的代碼是否安全,使用'il [0] == OpCodes.Ret.Value'? https://gist.github.com/vcsjones/5310717 – 2013-04-04 14:28:03
是的。雖然,你給我的想法是用OpCodes.Nop.Value取代我的0。謝謝! – Ani 2013-04-04 14:30:04
@Mahdi這跟我的方法差不多,只是附上了一個優雅的解釋(這就是我投票的原因)。他只是懶得去檢查最後一個操作,因爲它幾乎必須是'ret'。我只是確保每條指令都是nop並檢查最後一條是否被ret。 – vcsjones 2013-04-04 14:30:42
.Net中的代表只是函數指針而已。就像你不能說出.Net方法中有多少C#語句一樣,你不能確定.Net代理中有多少條語句。部分原因是因爲該方法不一定以C#或任何其他語言編碼的語言編碼。它可以直接寫在基於操作碼的IL中
編輯: 決定,而我等待我的構建服務器重新啓動觸摸這個一點點......
我確實有一些躺在附近我的「工具庫」是八九不離十,kinda-也許可以幫助你 - 還有很多需要改進的地方在這裏,自然 - 這從來就不是「生產使用」:
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
這是一個*很多*容易,如果你的拉姆達是一個表達式,而不是委託(以後它可以編譯爲代表) - 是一種可能性? – vcsjones 2013-04-04 14:08:11
你想要做什麼? – 2013-04-04 14:08:57
@vcsjones對不起,我不能用這個表達式。 – 2013-04-04 14:09:34