几个Caller-特性的妙用

System.Runtime.CompilerServices命名空间下有4个以“Caller”为前缀命名的Attribute,我们可以将它标注到方法参数上自动获取当前调用上下文的信息,比如当前的方法名、某个参数的表达式、当前源文件的路径,以及当前代码在源文件中的行号 。
一、CallerMemberNameAttribute顾名思义,如果当我们将CallerMemberNameAttribute特性标注到“可缺省参数”上,调用方无需显式指定参数值就可以将表示当前调用方法名赋值给该参数 。如下面的代码片段所示 , 我们为ActivitySource定义了一个名为StartNewActivity的扩展方法,表示Activity名称的name参数是一个“可缺省参数” 。我们在该参数上标准了CallerMemberNameAttribute特性,意味着当前调用的方法名将自动作为参数值 。
public static class Extensions{    public static Activity? StartNewActivity(this ActivitySource activitySource, ActivityKind kind = ActivityKind.Internal, [CallerMemberName] string name = "")   => activitySource.StartActivity(name: name, kind: kind);}以Activity/ActivitySource/ActivityListener为核心的模型实际上是对OpenTelemetry的实现,所有我们可以利用上面定义的这个StartNewActivity创建一个代码跟踪操作的Activity(对应OpenTelemetry下的Span) 。针对StartNewActivity方法调用体现在如下这个Invoker类型中 , 它的构造函数中注入了ActivitySource 对象 。InvokeAsync方法内部调用了私有方法FooAsync、后者又调用了BarAsync方法,调用链InvokeAsync->FooAsync->BarAsync的跟踪通过调用ActivitySource的StartNewActivity扩展方法被记录下来 , 我们在调用此方法时并没有指定参数 。
public class Invoker{    private readonly ActivitySource _activitySource;    public Invoker(ActivitySource activitySource) => _activitySource = activitySource;    public async Task InvokeAsync()    {        using (_activitySource.StartNewActivity())        {            await Task.Delay(100);            await FooAsync();        }    }    private async Task FooAsync()    {        using (_activitySource.StartNewActivity())        {            await Task.Delay(100);            await BarAsync();        }    }    private Task BarAsync()    {        using (_activitySource.StartNewActivity())        {            return Task.Delay(100);        }    }}我们利用如下的代码利用依赖注入框架将Invoker对象创建出来,并调用其Invoke方法 。
ActivitySource.AddActivityListener(new ActivityListener{    ShouldListenTo = _ => true,    Sample = (ref ActivityCreationOptions<ActivityContext> options) => ActivitySamplingResult.AllData,    ActivityStopped = activity => {        Console.WriteLine(activity.DisplayName);        Console.WriteLine($"\tTraceId:{activity.TraceId}");        Console.WriteLine($"\tSpanId:{activity.SpanId}");        Console.WriteLine($"\tDuration:{activity.Duration}");        foreach (var kv in activity.TagObjects)        {            Console.WriteLine($"\t{kv.Key}:{kv.Value}");        }        Console.WriteLine();    }});await new ServiceCollection()   .AddSingleton(new ActivitySource("App"))   .AddSingleton<Invoker>()   .BuildServiceProvider()   .GetRequiredService<Invoker>()   .InvokeAsync();

推荐阅读