{"record":{"id":"defe1e130f91c236","repo":"dotnet/BenchmarkDotNet","slug":"please-use-targets-property","errorCode":null,"errorMessage":"Please use Targets property","messagePattern":"Please use Targets property","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"info","filePath":"src/BenchmarkDotNet.Annotations/Attributes/TargetedAttribute.cs","lineNumber":17,"sourceCode":"using System.Reflection;\n\nnamespace BenchmarkDotNet.Attributes\n{\n    /// <summary>\n    /// Base class for attributes that are targeted at one or more method(s)\n    /// </summary>\n    public abstract class TargetedAttribute : Attribute\n    {\n        public string[] Targets { get; set; } = [];\n\n        /// <summary>\n        /// Target method for attribute\n        /// </summary>\n        public string Target\n        {\n            get => throw new InvalidOperationException(\"Please use Targets property\"); // kept to keep compiler happy \"Named attribute arguments must be fields which are not readonly, static, or const, or read-write properties which are public and not static.\"\n            set => Targets = string.IsNullOrEmpty(value) ? [] : value.Split(','); // , is for backward compat\n        }\n\n        public bool Match(MethodInfo method) => Targets.Length == 0 || Targets.Contains(method.Name);\n    }\n}\n","sourceCodeStart":1,"sourceCodeEnd":24,"githubUrl":"https://github.com/dotnet/BenchmarkDotNet/blob/b515068b61ad1c9c9aa938b8ece4af1e7d6d85a3/src/BenchmarkDotNet.Annotations/Attributes/TargetedAttribute.cs#L1-L24","documentation":"TargetedAttribute is the base class for targeting attributes such as [Benchmark]. The library migrated the single-method 'Target' string property to a 'Targets' string[] array. The 'Target' getter is preserved only because C# attribute syntax requires a read-write property pair; calling the getter throws to redirect consumers to the 'Targets' array. The setter still works (it splits on ',' and stores into Targets) for backward compatibility.","triggerScenarios":"Programmatically reading the 'Target' property via reflection at runtime (e.g. typeof(X).GetCustomAttributes().Target, or iterating attribute properties). Happens in custom tooling that inspects [Benchmark] / [ParamsSource]-style targeted attributes, or in code ported from an older BDN version that read '.Target'.","commonSituations":"Upgrading BenchmarkDotNet past the Target->Targets rename while keeping code that accesses the old single-value property; building analyzers/utilities that reflect over benchmark attributes.","solutions":["Read the 'Targets' string[] property instead of 'Target': attribute.Targets.Contains(methodName).","When writing new attributes, set [Benchmark(Target = \"Foo\")] is fine for input; just never call the getter.","If reflecting, filter for the 'Targets' property name and skip 'Target'."],"exampleFix":"// before\nvar name = ((TargetedAttribute)attr).Target; // throws\n\n// after\nvar names = ((TargetedAttribute)attr).Targets;\nvar match = names.Length == 0 || names.Contains(\"MyMethod\");","handlingStrategy":"validation","validationCode":"// Before reading, branch on Targets and never touch the Target getter:\nstatic bool IsTargeted(TargetedAttribute attr, string methodName)\n    => attr.Targets.Length == 0 || attr.Targets.Contains(methodName);","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Never read attribute.Target; only set it (via attribute syntax) or read attribute.Targets.","When reflecting over attributes, special-case the string 'Target' to skip its getter."],"tags":["benchmarkdotnet","attributes","reflection","api-migration"],"backgroundTag":null,"analyzedSha":"b515068b61ad1c9c9aa938b8ece4af1e7d6d85a3","analyzedAt":"2026-08-13T19:12:24.196Z","schemaVersion":2},"datasetVersion":"2026-08-14T00:17:13.853Z"}