Runtime & concurrency
using System;
using System.Linq;
using System.Collections.Generic;
using System.Collections.Concurrent;
using System.Reflection;
using System.Linq.Expressions;
using System.Threading;
using System.Threading.Tasks;
var data = CustomAttributeData.GetCustomAttributes(typeof(Annotated)).Single(item => item.AttributeType == typeof(MarkerAttribute));
Console.WriteLine($"data:{data.ConstructorArguments[0].Value}");
Console.WriteLine($"constructors:{MarkerAttribute.Created}");
var instance = (MarkerAttribute)Attribute.GetCustomAttribute(typeof(Annotated), typeof(MarkerAttribute))!;
Console.WriteLine($"instance:{instance.Name}");
Console.WriteLine($"constructors:{MarkerAttribute.Created}");
[Marker("demo")] sealed class Annotated { }
[AttributeUsage(AttributeTargets.Class)]
sealed class MarkerAttribute : Attribute
{
public static int Created;
public string Name { get; }
public MarkerAttribute(string name) { Name = name; Created++; }
}
Reveal answer and reasoning
The lines are data:demo, constructors:0, instance:demo, and constructors:1. The first query selects MarkerAttribute's recorded data and reads the literal demo argument without constructing a MarkerAttribute. Accessing the counter then reports its unchanged zero. The live retrieval invokes the constructor, which stores Name and increments Created once, making the final two observations demo and one.
The annotation does not instantiate the annotated class or modify it, and reading its Type does not authorize treating every attribute reader as passive. The counter is a deliberate teaching side effect, not an application policy. A common wrong trace increments it during metadata inspection or claims the annotation must have run during class loading. The explicit API distinction explains the result without relying on metadata enumeration order, because the query filters the exact custom attribute type before selecting its constructor argument.
What a strong answer covers
- CustomAttributeData reads demo without construction.
- The counter remains zero until instance retrieval.
- The final count is one in this fresh execution.
Common pitfall
Counting a metadata read as an executed custom attribute constructor.
Follow-up questions
- What would a second live retrieval need you to observe?
- Why does the query filter AttributeType?
Self-review
Choose what to revisit. These marks are your own assessment and are separate from lesson completion.