C#
dynamic attributes
programming
software development
C# attributes

Can attributes be added dynamically in C?

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

In C#, attributes are metadata emitted into assembly metadata at compile time. Because that metadata is baked into compiled artifacts, you cannot truly add attributes to existing types or members dynamically at runtime in the same assembly image.

This often surprises developers trying to drive behavior from runtime state. The good news is there are practical alternatives: dynamic proxy metadata, custom registries, source generation, and runtime type emission for advanced scenarios.

Core Sections

1. Why runtime attribute mutation is not supported

Attributes are compiled into metadata tables. Reflection reads those tables but does not mutate them for loaded types.

csharp
1[Obsolete("Use NewApi")]
2public class OldApi {}
3
4// Reflection can read this, but cannot append new attributes to OldApi at runtime.
5var attrs = typeof(OldApi).GetCustomAttributes(inherit: false);

Even if you use reflection emit in some scenarios, you are creating new dynamic types, not modifying existing compiled definitions.

2. Practical alternatives for dynamic behavior

If you need "attribute-like" runtime decisions, use a metadata registry.

csharp
1using System;
2using System.Collections.Concurrent;
3
4public static class RuntimeMetadata
5{
6    private static readonly ConcurrentDictionary<(Type, string), object> Store = new();
7
8    public static void Set<T>(Type target, string key, T value)
9        => Store[(target, key)] = value!;
10
11    public static bool TryGet<T>(Type target, string key, out T value)
12    {
13        if (Store.TryGetValue((target, key), out var obj) && obj is T typed)
14        {
15            value = typed;
16            return true;
17        }
18        value = default!;
19        return false;
20    }
21}

This pattern is explicit, mutable, and testable.

Another option is policy interfaces instead of attributes:

csharp
1public interface IValidationPolicy
2{
3    bool IsAllowed(object value);
4}

Then resolve policy from DI container dynamically.

3. Compile-time generation for "dynamic-like" flexibility

If metadata must look like attributes to consumers, consider source generators during build time.

High-level approach:

  1. define declarative config (JSON/YAML/code),
  2. source generator emits partial classes with attributes,
  3. compile and consume via normal reflection.

This preserves attribute semantics while still allowing flexible configuration upstream of compilation.

4. Runtime type emission edge case

With System.Reflection.Emit, you can create new dynamic types and attach attributes at creation time.

csharp
// conceptually: create dynamic assembly/type and apply CustomAttributeBuilder

But this is niche, complex, and usually unnecessary for business apps. It also does not alter existing types already loaded from assemblies.

Common Pitfalls

  • Assuming reflection APIs can persistently mutate attributes on compiled types.
  • Forcing attribute-based patterns for runtime decisions better modeled as configuration/policies.
  • Using fragile custom reflection hacks instead of explicit metadata registries.
  • Introducing Reflection.Emit complexity where simple DI/config solutions would suffice.
  • Confusing "new dynamic type with attributes" and "modifying attributes on existing type."

Summary

C# attributes are compile-time metadata and cannot be dynamically added to existing types at runtime. For runtime-driven behavior, use explicit metadata registries, policy abstractions, or DI-based strategies. If attribute presence is mandatory, use compile-time generation instead of runtime mutation.

If existing frameworks require attribute-based configuration (for example validation or mapping), but your rules must change at runtime, introduce an adapter layer that reads both static attributes and dynamic policy sources. This preserves compatibility while enabling runtime flexibility. Keep precedence rules explicit so conflicts between static metadata and dynamic configuration are deterministic.

For highly dynamic scenarios, reconsider whether attribute-driven design is still appropriate. Attributes are excellent for stable declarative metadata, but runtime policy engines, rules tables, or feature flag systems are often better for mutable behavior. Choosing the right abstraction reduces complexity and avoids fighting language/runtime constraints.

This mindset shift from metadata mutation to explicit runtime policy usually leads to cleaner, more predictable systems.

Explicit runtime policies scale better than hidden metadata hacks.


Course illustration
Course illustration

All Rights Reserved.