<style> .reveal-up, .reveal-left, .reveal-right, .reveal-stagger > * { opacity: 1 !important; transform: none !important; } </style>
  1. Learn
  2. C#
  3. Advanced C#
All lessons in this course

Lesson 51 of 60 13 minAdvanced

Reflection and attributes

Inspect types at run time, attach metadata with attributes, and build things like validators, serializers and plug-in loaders.

Reflection lets a program examine types, properties and methods while it runs. Attributes are labels in square brackets that attach extra information to code. Together they power serializers (JSON), validation, ORMs like Entity Framework, test frameworks and plug-in systems.

Interactive

Code that reads code

Beam.cs
public class Beam{    [Required, MaxLength(6)]    public string Mark { get; set; } = "";     [Required]    public string Profile { get; set; } = "";     [Range(0.5, 24)]    public double LengthM { get; set; }     public int Level { get; set; }}
typeof(Beam).GetProperties()
Mark : String
[Required][MaxLength(6)]
Profile : String
[Required]
LengthM : Double
[Range(0.5, 24)]
Level : Int32
no attributes
var errors = Validator.Check(beam); // loops over properties and their attributes
Valid: every attribute rule passed.

Inspecting a type

Inspect.cs
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 it
Output
Beam
Mark : String
Profile : String
LengthM : Double
Level : Int32
6

Attributes

Attributes.cs
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;}

You have already used attributes: [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

Validator.cs
[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 })));
Output
Count must be positive

Creating objects and calling methods dynamically

Plugins.cs
// 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 PropertyInfo objects, and don’t use reflection in hot loops.
  • It skips compile-time checks: a typo in GetProperty("Lenght") returns null at 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.
Quick check

What does an attribute like [Range(0.5, 24)] do on its own?

Quick check

How do you get the Type object of an existing object x?

Practice

Write 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
Solution.cs
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 }));
Output
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.
0 of 60 lessons completed