0Pricing
C# Academy · Lesson

Light metaprogramming scenarios

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

Light metaprogramming scenarios is a free C# Academy lesson on CoddyKit — 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, one of 3 lessons in the course, and your progress syncs across the web and the CoddyKit app.

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.

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, and the C# Academy course includes 3 lessons in total. 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.

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; this is — lesson 3 of 3, so you can start here or from the beginning and move at your own pace.

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