Reflection and attributes
Inspect types at run time, attach metadata with attributes, and build things like validators, serializers and plug-in loaders.
Inspecting a type
using System.Reflection; Type t = typeof(Beam); // or obj.GetType()Console.WriteLine(t.FullName); foreach (PropertyInfo p in t.GetProperties()) Console.WriteLine($"{p.Name} : {p.PropertyType.Name}"); var beam = new Beam { Mark = "B1", LengthM = 6 };PropertyInfo prop = t.GetProperty("LengthM")!;Console.WriteLine(prop.GetValue(beam)); // read a property by nameprop.SetValue(beam, 7.5); // and write itBeam Mark : String Profile : String LengthM : Double Level : Int32 6
Attributes
using System.ComponentModel.DataAnnotations; public class Beam{ [Required, MaxLength(6)] public string Mark { get; set; } = ""; [Range(0.5, 24)] public double LengthM { get; set; } [Obsolete("Use LengthM")] // compiler warns anyone who uses it public double Length => LengthM;}[Flags], [JsonIgnore], [JsonPropertyName]. On their own attributes do nothing. They are metadata that some code (the compiler, a serializer, your own validator) reads via reflection.
Your own attribute and a tiny validator
[AttributeUsage(AttributeTargets.Property)]public sealed class PositiveAttribute : Attribute { } public class Column{ [Positive] public double HeightM { get; set; } [Positive] public int Count { get; set; }} static List<string> Validate(object obj){ var errors = new List<string>(); foreach (var p in obj.GetType().GetProperties()) { if (p.GetCustomAttribute<PositiveAttribute>() is null) continue; double value = Convert.ToDouble(p.GetValue(obj)); if (value <= 0) errors.Add($"{p.Name} must be positive"); } return errors;} Console.WriteLine(string.Join("; ", Validate(new Column { HeightM = 3.2, Count = 0 })));Count must be positive
Creating objects and calling methods dynamically
// Load every class implementing IExporter from a plug-in DLLAssembly asm = Assembly.LoadFrom("Exporters.dll");var exporters = asm.GetTypes() .Where(t => typeof(IExporter).IsAssignableFrom(t) && !t.IsAbstract) .Select(t => (IExporter)Activator.CreateInstance(t)!) .ToList();Costs and cautions
Reflection is slow compared to normal calls. Cache PropertyInfoobjects, and don’t use reflection in hot loops.It skips compile-time checks: a typo in GetProperty("Lenght")returnsnullat run time.It can read private members, which breaks encapsulation. Use with care. Trimming and native AOT can remove members only used via reflection. Source generators (Master module) are the modern, fast alternative for many reflection uses.
static string Describe(object o) that uses reflection to return “TypeName { Prop1 = …, Prop2 = … }” for any object’s public properties. Test it on an anonymous object new { Mark = "B1", Kg = 253 }.
Show the solution
static string Describe(object o){ var t = o.GetType(); var parts = t.GetProperties().Select(p => $"{p.Name} = {p.GetValue(o)}"); string name = t.Name.StartsWith("<>") ? "Anonymous" : t.Name; return $"{name} {{ {string.Join(", ", parts)} }}";} Console.WriteLine(Describe(new { Mark = "B1", Kg = 253 }));Anonymous { Mark = B1, Kg = 253 }Remember
Reflection inspects types and members at run time ( GetType,GetProperties,GetValue).Attributes attach metadata; code reads them with GetCustomAttribute.Frameworks (JSON, validation, EF, tests) are built on reflection + attributes. Reflection is slow and unchecked: cache results and use it sparingly.