【发布时间】:2016-11-17 10:49:57
【问题描述】:
我正在尝试确定某个特定方法是否被 async 方法调用。
This answer(当然,它描述了一组稍微不同的情况)建议使用CallerMemberName 属性来查找调用方法的名称。事实上,我的方法的签名是这样的:
public void LogCallAsync([CallerMemberName] string caller = "", params object[] parameters)
如果你正在做类似的事情,效果很好
logger.LogCallAsync();
如果您有固定数量的参数,它也会很好用。但是,鉴于下一个参数的类型为params object[],显然情况并非如此,所以如果您尝试执行类似的操作
logger.LogCallAsync(someObject, someOtherObject)
我会得到一个编译异常,因为someObject 不是字符串。我尝试了以下解决方法:
logger.LogCallAsync(nameof(CurrentMethod), someObject, someOtherObject);
这很丑陋。实际上,如果我 不必 必须这样做,我会更喜欢它,所以如果有人在这方面有任何建议,那就太好了,但我的主要问题是:有什么方法可以防止人们不正确地调用它?特别是,我想知道“CurrentMethod”是否是,实际上是一个实际的async 方法。
例如,如果我查看StackTrace 的实例,是否有基于此的可靠判断方法? This article(如果我没看错的话)似乎暗示我当前的解决方案(见下面我的代码示例)是正确的,但它并不是真正的“权威”来源,此时它已经有 5 年的历史了。
让我展示一个代码示例来说明我现在是如何解决这个问题的:
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();
}
我正在使用 Visual Studio 2015 和 .NET 4.6 来获得它的价值。那么,我的问题是:我能否保证代码始终以类似于我上面的方式工作?例如,如果 GetCaller 被异步方法调用,我是否会总是将 MoveNext 从堆栈跟踪中删除?另外,有谁知道微软是否在某处记录了这一点(以防我被要求证明我的解决方案有效)?
编辑:这里的主要目的是记录调用记录器的方法的名称和参数。如果调用者是不是异步的,那么我知道下面的代码
StackTrace stack = new StackTrace();
MethodBase method = stack.GetFrame(1).GetMethod();
会告诉我来电者是谁。但是,如果调用者是async,那显然根本行不通。在这种情况下,我目前执行以下操作:
StackTrace st = new StackTrace();
StackFrame callFrame = st.GetFrames().ToList().FirstOrDefault(x => x.GetMethod().Name == caller);
其中caller 是我传递的方法调用的名称,就像我在上面列出的签名中一样:
public void LogCallAsync([CallerMemberName] string caller = "", params object[] parameters)
显然,这效率较低。理论上,我可以对每个记录器调用都这样做,但这会对性能造成一点影响,如果可能的话,我宁愿避免这样做。
【问题讨论】:
-
你的问题写满了XY Problem。我很难相信知道你的调用者是否实际上是一个声明为
async的方法是有用的,没关系。方法可能看起来像async-declared 方法,但实际上并不是一个方法,而其他方法的行为可能与async方法相似(即以您可能真正关心的方式),但看起来并不像一个。您真正想要解决的问题是什么? -
@PeterDuniho 这可能是一个 XY 问题案例,如果我采取了错误的方法,那肯定也很高兴知道。我会尽快编辑以包含更多详细信息。
-
@PeterDuniho 我编辑了 - 这是否澄清了这一点?
-
请向minimal reproducible example 提供一些示例,这些示例可以完全展示您正在尝试做什么,包括不同的调用以及您想要获得的输出以及代码输出的精确描述。请注意,这里没有理由介绍
AsyncPump类;出于示例的目的,您可以在返回的Task上调用Wait()(尽管可能不希望在生产代码中这样做)。 -
@EJoshuaS:你可能会发现我的async diagnostics 包很有用。它不依赖于任何未记录的行为。
标签: c# .net reflection async-await .net-4.6