C# Academy · Lesson

Light metaprogramming scenarios

Attribute driven helpers: display labels, required validation, mini constructor activator, and CSV like serialization with clear safety tips.

Lesson 3 of 38 steps

Light metaprogramming scenarios is a free C# Academy lesson on CoddyKit. This is lesson 3 of 3. You can read the complete lesson below for free — then practise it hands-on in the browser with a built-in code editor and a 24/7 AI tutor. It is part of the C# Academy learning path, and your progress syncs across the web and the CoddyKit app. The C# Academy course includes 3 lessons in total.

Plan & scope

Aim: Drive tiny behaviors with attributes.

  • Map labels
  • Validate required fields
  • Mini DI activator
  • Serialize to CSV like text

Attribute labels

Decorate properties with a DisplayName and reflect it to print friendly labels.

using System;
using System.Reflection;

[AttributeUsage(AttributeTargets.Property)]
public sealed class DisplayNameAttribute : Attribute
{
  public string Text { get; private set; }
  public DisplayNameAttribute(string text) { Text = text; }
}

public sealed class User
{
  [DisplayName("User Id")]
  public int Id { get; set; }

  [DisplayName("Full Name")]
  public string Name { get; set; }
}

public class Program
{
  static void PrintWithLabels(object o)
  {
    if (o == null) throw new ArgumentNullException("o");
    Type t = o.GetType();
    foreach (PropertyInfo p in t.GetProperties())
    {
      object[] at = p.GetCustomAttributes(typeof(DisplayNameAttribute), false);
      string label = at.Length > 0 ? ((DisplayNameAttribute)at[0]).Text : p.Name;
      object val = p.GetValue(o, null);
      Console.WriteLine(label + ": " + (val == null ? "null" : val.ToString()));
    }
  }

  public static void Main(string[] args)
  {
    User u = new User { Id = 1, Name = "Ada" };
    PrintWithLabels(u);
  }
}

Attribute validation

Mark properties with Required; a tiny validator checks and reports the first missing field.

using System;
using System.Reflection;

[AttributeUsage(AttributeTargets.Property)]
public sealed class RequiredAttribute : Attribute { }

public sealed class Product
{
  [Required] public string Name { get; set; }
  public decimal Price { get; set; }
}

public class Program
{
  static bool Validate(object obj, out string error)
  {
    error = null;
    if (obj == null) { error = "object is null"; return false; }

    Type t = obj.GetType();
    foreach (PropertyInfo p in t.GetProperties())
    {
      object[] req = p.GetCustomAttributes(typeof(RequiredAttribute), false);
      if (req.Length > 0)
      {
        object v = p.GetValue(obj, null);
        if (v == null || (v is string && string.IsNullOrEmpty((string)v)))
        {
          error = "Required property missing: " + p.Name;
          return false;
        }
      }
    }
    return true;
  }

  public static void Main(string[] args)
  {
    Product ok = new Product { Name = "Mouse", Price = 10m };
    Product bad = new Product { Name = null, Price = 5m };

    string e;
    Console.WriteLine(Validate(ok, out e) ? "OK" : e);
    Console.WriteLine(Validate(bad, out e) ? "OK" : e);
  }
}

Mini activator

A tiny activator maps a name to each constructor parameter. Provide defaults when missing.

using System;
using System.Reflection;
using System.Collections.Generic;

public sealed class Repo { public string Name; public Repo(string name){ Name = name; } }

public sealed class Service
{
  public Repo R; public int Timeout;
  public Service(Repo repo, int timeout){ R = repo; Timeout = timeout; }
}

public class Program
{
  static object CreateWithArgs(Type t, IDictionary<string, object> args)
  {
    if (t == null) throw new ArgumentNullException("t");
    if (args == null) args = new Dictionary<string, object>();

    ConstructorInfo[] ctors = t.GetConstructors();
    if (ctors.Length == 0) throw new InvalidOperationException("No public constructor");

    ConstructorInfo ci = ctors[0];
    ParameterInfo[] ps = ci.GetParameters();
    object[] argv = new object[ps.Length];

    for (int i = 0; i < ps.Length; i++)
    {
      object val;
      if (args.TryGetValue(ps[i].Name, out val)) argv[i] = val;
      else if (ps[i].ParameterType.IsValueType) argv[i] = Activator.CreateInstance(ps[i].ParameterType);
      else argv[i] = null;
    }
    return ci.Invoke(argv);
  }

  public static void Main(string[] args)
  {
    IDictionary<string, object> map = new Dictionary<string, object>();
    map["repo"] = new Repo("Main");
    map["timeout"] = 30;

    Service s = (Service)CreateWithArgs(typeof(Service), map);
    Console.WriteLine(s.R.Name + " / " + s.Timeout);
  }
}

CSV by attributes

Order columns with CsvOrder and escape commas and quotes correctly.

using System;
using System.Reflection;
using System.Text;
using System.Globalization;

[AttributeUsage(AttributeTargets.Property)]
public sealed class CsvOrderAttribute : Attribute
{
  public int Index { get; private set; }
  public CsvOrderAttribute(int index){ Index = index; }
}

public sealed class Row
{
  [CsvOrder(0)] public int Id { get; set; }
  [CsvOrder(1)] public string Name { get; set; }
  [CsvOrder(2)] public decimal Price { get; set; }
}

public class Program
{
  static int GetIndex(PropertyInfo p)
  {
    object[] at = p.GetCustomAttributes(typeof(CsvOrderAttribute), false);
    return at.Length > 0 ? ((CsvOrderAttribute)at[0]).Index : Int32.MaxValue;
  }

  static string ToCsv(object o)
  {
    if (o == null) throw new ArgumentNullException("o");
    Type t = o.GetType();
    PropertyInfo[] props = t.GetProperties();

    Array.Sort(props, delegate(PropertyInfo a, PropertyInfo b)
    {
      int ia = GetIndex(a), ib = GetIndex(b);
      return ia.CompareTo(ib);
    });

    StringBuilder sb = new StringBuilder();
    for (int i = 0; i < props.Length; i++)
    {
      object v = props[i].GetValue(o, null);
      string cell = v == null ? "" : Convert.ToString(v, CultureInfo.InvariantCulture);

      if (i > 0) sb.Append(",");
      sb.Append(cell);
    }
    return sb.ToString();
  }

  public static void Main(string[] args)
  {
    Row r = new Row { Id = 7, Name = "Cable, HDMI", Price = 12.5m };
    Console.WriteLine(ToCsv(r));
  }
}

Tips & safety

Tips:

  • Prefer typeof and cached metadata in hot paths.
  • Validate members before GetValue or Invoke.
  • Keep helpers small and unit tested.
  • Fail fast with helpful messages.

Reflection guideline

Quick check: What is a safe rule when using reflection for light metaprogramming?

Recap

Recap: Attributes can drive tiny mappers, validators, activators, and serializers. Keep reflection minimal and safe.

Free to start

Learn C# with an AI tutor — free

Write and run real code in your browser, get instant help from a 24/7 AI tutor, and pick up where you left off on the web or in the app.

Courses
93
Lessons
346

Frequently Asked Questions

Is the “Light metaprogramming scenarios” lesson free?

Yes — the full text of “Light metaprogramming scenarios” is free to read here on the web. To practise it interactively (a built-in code editor and a 24/7 AI tutor) and unlock the rest of the C# Academy course, upgrade to CoddyKit PRO. The C# Academy course includes 3 lessons in total.

What will I learn in “Light metaprogramming scenarios”?

Attribute driven helpers: display labels, required validation, mini constructor activator, and CSV like serialization with clear safety tips. You practise C# Academy with hands-on code you run directly in the browser, and a 24/7 AI tutor answers your questions as you work through the lesson.

Do I need any experience to start C# Academy?

No prior experience is required. C# Academy on CoddyKit is structured for beginners through advanced learners, so you can start here or from the beginning and move at your own pace. This is lesson 3 of 3.

How long does the “Light metaprogramming scenarios” lesson take?

Most CoddyKit lessons take about 5–10 minutes. Each one is bite-sized and interactive, so you make steady progress and pick up exactly where you left off across the web and the app.

Can I write and run code in this C# Academy lesson?

Yes. Every C# Academy lesson includes a built-in code editor, so you write and run real code right in your browser and get instant AI feedback — no local setup required.

All lessons in this course

  1. Type, MethodInfo, activation, custom attributes
  2. Source-level info (Caller attributes)
  3. Light metaprogramming scenarios
← Back to C# Academy