I am trying to determine whether a particular method was called by an async
method.
This answer (which was, granted, describing a somewhat different set of circumstances) suggested using CallerMemberName
attribute to find the name of the calling method. Indeed, the signature of my method looks like this:
public void LogCallAsync([CallerMemberName] string caller = "", params object[] parameters)
which works great if you're doing something like
logger.LogCallAsync();
It would also work great if you have a fixed number of parameters. However, given that the next parameter is of type params object[]
, obviously that isn't the case, so if you try to do something like
logger.LogCallAsync(someObject, someOtherObject)
I'll get a compile exception because someObject
isn't a string. I tried the following as a workaround:
logger.LogCallAsync(nameof(CurrentMethod), someObject, someOtherObject);
which is pretty ugly. Actually, I'd prefer it if I didn't have to do that, so if anyone has any suggestions in that regard that would be great, but on to my main question: is there some way of preventing people from calling this improperly? In particular, I'd like to know if "CurrentMethod" is, in fact, an actual async
method.
If I look at an instance of StackTrace
, for example, is there a reliable way of telling based on that? This article (if I'm reading it correctly) seems to imply that my current solution (see my code sample below) is correct, but it's not really an "authoritative" source and it's about 5 years old at this point.
Let me show a code sample to illustrate how I'm trying to solve this right now:
private static void GetCaller()
{
StackTrace stack = new StackTrace();
MethodBase method = stack.GetFrame(1).GetMethod();
Trace.TraceInformation("Method name: " + method.Name);
}
// Some arbitrary async method
private static async Task UseReflection()
{
// Do some kind of work
await Task.Delay(100);
// Figure out who called this method in the first place
// I want some way of figuring out that the UseReflection method is async
GetCaller();
}
static void Main(string[] args)
{
// AsyncPump is basically to make Async work more like it does on a UI application
// See this link: https://blogs.msdn.microsoft.com/pfxteam/2012/01/20/await-synchronizationcontext-and-console-apps/
AsyncPump.Run(async () =>
{
// In this case, it identifies the calling method as "MoveNext"
// Question: in cases like this, will this always be the case (i.e. will it always be MoveNext)?
await UseReflection();
});
// In this case, it identifies the calling method as "Main"
GetCaller();
}
I'm using Visual Studio 2015 and .NET 4.6 for what it's worth. My question, then, is: can I guarantee that code will always work in a way that's similar to what I have above? For example, if GetCaller
was called by an async method, will I always get MoveNext
off the stack trace? Also, does anyone know if Microsoft documents that somewhere (in case I'm asked to prove that my solution will work)?
EDIT: The primary purpose here is to log the name and parameters of the method that called the logger. If the caller is not async, then I know that the following code
StackTrace stack = new StackTrace();
MethodBase method = stack.GetFrame(1).GetMethod();
will give me information on who the caller is. However, that obviously won't work at all if the caller is async
. In that case I currently do the following:
StackTrace st = new StackTrace();
StackFrame callFrame = st.GetFrames().ToList().FirstOrDefault(x => x.GetMethod().Name == caller);
where caller
is the name of the method call that I passed, like in the signature I listed above:
public void LogCallAsync([CallerMemberName] string caller = "", params object[] parameters)
Obviously, this is less efficient. I could, in theory, just do this for every logger call, but it would be a little bit of a performance hit and I'd rather avoid that if possible.