C# under the covers
In an earlier exploration of debug build code patterns I unearthed bits of debug assistance built into simple field-backed getter properties in C#.
That same pattern -- evaluate to a temporary, and then unconditionally branch to a return -- also shows up in non-property methods that return a constant literal or a field. With purely empty methods, there is no such intermediate value to expose to the debugger, so the method just looks like
.method public hidebysig static void UnitStub() cil managed | |
{ | |
.maxstack 8 | |
L_0000: nop | |
L_0001: ret | |
} |
i.e. a block with a Nop and a Return (with no expression).
The canonical stub left by Visual Studio implementing an interface looks like
public static int ThrowsStub(int left, string mid, object right) | |
{ | |
throw new NotImplementedException(); | |
} |
which resolves to
.method public hidebysig static int32 ThrowsStub(int32 left, string mid, object right) cil managed | |
{ | |
.custom instance void .maxstack 8 | |
L_0000: nop | |
L_0001: newobj instance void [mscorlib]System.NotImplementedException::.ctor() | |
L_0006: throw | |
} |
i.e. a block with a Nop and a Throw (with a Construct expression).
Nop and a Throw alone will catch any method that simply throws as its one and only operation (even if the exception is passed in from somewhere else); if you want to determine what is being thrown, then dissecting the expression would be required.
In writing a simple static analysis tool, identifying anything that just throws -- and has no decision making logic -- is sufficient for my purposes; for anyone tempted to put massive lambdas in the throw expression, testing for the Construct may be worthwhile.
No comments :
Post a Comment