The official page for Assembly.GetCallingAssembly()
says that the calling method should not be inlined, and lists the following example as a remark:
Method M1 in assembly A1 calls GetCallingAssembly.
Method M2 in assembly A2 calls M1.
Method M3 in assembly A3 calls M2.
When M1 is not inlined, GetCallingAssembly returns A2. When M1 is inlined, GetCallingAssembly returns A3. Similarly, when M2 is not inlined, GetCallingAssembly returns A2. When M2 is inlined, GetCallingAssembly returns A3.
What's not clear to me from this is: do both M1
and M2
need to not be inlined or just one of them? The wording is somewhat confusing because it starts by saying:
When M1 is not inlined, GetCallingAssembly returns A2.
But immediately after it also says:
When M2 is inlined, GetCallingAssembly returns A3.
The statements are not necessarily exclusive, so should one read this as:
When M1 is not inlined *and* M2 is not inlined, GetCallingAssembly returns A2
Or should it be read as:
When M1 is not inlined, GetCallingAssembly returns A2 regardless of whether M2 is inlined or not
?
Assuming it's former, what happens when you add a M3
and M4
and M5
methods from all different assemblies, does the whole chain need to be marked with [MethodImpl(MethodImplOptions.NoInlining)]
or where does it stop?
This is made somewhat more confusing because the code example from the official documentation has both methods marked with [MethodImpl(MethodImplOptions.NoInlining)]
, but the example above makes no reference to what happens when M3
is inlined (regardless of whether M1
or M2
are inlined or not)
CodePudding user response:
Consider M3.A3 -> M2.A2 -> M1.A1 -> GetCallingAssembly - we expect to see A2 because we're looking back one level
Now consider the impact of inlining:
- M1.A1 inlined: M3.A3 -> M2.A2 -> GetCallingAssembly - we could see A3
- M2.A2 inlined: M3.A3 -> M1.A1 -> GetCallingAssembly - we could see A3
- M1.A1 and M2.A2 inlined: M3.A3 -> GetCallingAssembly - we could see (another unknown level of caller)
So ... indeed, GetCallingAssembly
is a little susceptible when it comes to inlining; on a bad day, any number of methods on a call chain could be inlined (if they are all trivial pass-along methods). I think the only reliable guidance you can give here is: don't use GetCallingAssembly
for anything too brittle or security-critical. I don't recommend adding too many no-inlining hints, as that could be impactful for performance-criticial code. Perhaps find other - more reliable - ways of achieving what you're trying to achieve here, for example by allowing the caller to pass in a type or assembly to use to infer that information.