programming
C#
attributes
data-types
compiler-errors

Why decimal is not a valid attribute parameter type?

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 stored as metadata, not as arbitrary runtime objects. That is why attribute constructor arguments and named property values are limited to a small set of types, and decimal is not one of them.

Why decimal Is Rejected

C# attributes compile into CLR custom attribute metadata. The metadata format supports only certain constant-like element types. Those include primitives such as int, bool, double, string, Type, enums, object, and one-dimensional arrays of supported types.

decimal is a built-in C# type, but it is not one of the attribute parameter types permitted by the metadata format. So this fails:

csharp
1using System;
2
3[AttributeUsage(AttributeTargets.Class)]
4public class PriceAttribute : Attribute
5{
6    public PriceAttribute(decimal amount)
7    {
8        Amount = amount;
9    }
10
11    public decimal Amount { get; }
12}
13
14[Price(12.5m)]
15public class Product
16{
17}

The compiler error is not saying decimal is unsupported in C# generally. It is saying decimal cannot be encoded as an attribute argument.

Why Other Constants Work

Primitive numeric types such as int and double are special here because the attribute system knows how to persist them directly into metadata.

This works:

csharp
1using System;
2
3[AttributeUsage(AttributeTargets.Class)]
4public class RetryAttribute : Attribute
5{
6    public RetryAttribute(int count)
7    {
8        Count = count;
9    }
10
11    public int Count { get; }
12}
13
14[Retry(3)]
15public class Worker
16{
17}

The difference is not whether the value is constant in your source code. The difference is whether the CLR attribute metadata format supports the type.

Practical Workarounds

Use double If Approximation Is Acceptable

If the attribute value is descriptive rather than financially exact, double may be enough.

csharp
1using System;
2
3[AttributeUsage(AttributeTargets.Class)]
4public class ThresholdAttribute : Attribute
5{
6    public ThresholdAttribute(double value)
7    {
8        Value = value;
9    }
10
11    public double Value { get; }
12}
13
14[Threshold(0.75)]
15public class Rule
16{
17}

This is fine for ratios or heuristic thresholds. It is usually the wrong choice for currency.

Store The Value As A String

If precision matters, encode the value as a string and parse it inside the attribute.

csharp
1using System;
2using System.Globalization;
3
4[AttributeUsage(AttributeTargets.Class)]
5public class PriceAttribute : Attribute
6{
7    public PriceAttribute(string amount)
8    {
9        Amount = decimal.Parse(amount, CultureInfo.InvariantCulture);
10    }
11
12    public decimal Amount { get; }
13}
14
15[Price("12.50")]
16public class Product
17{
18}

This is the most common workaround when exact decimal semantics are required.

Store Minor Units As An Integer

If the meaning is currency, another robust option is to store minor units such as cents.

csharp
1using System;
2
3[AttributeUsage(AttributeTargets.Class)]
4public class PriceInCentsAttribute : Attribute
5{
6    public PriceInCentsAttribute(int cents)
7    {
8        Cents = cents;
9    }
10
11    public int Cents { get; }
12
13    public decimal Amount => Cents / 100m;
14}
15
16[PriceInCents(1250)]
17public class Product
18{
19}

This avoids parsing and keeps the metadata representation simple.

Why C# Does Not Special-Case decimal

It is tempting to think the compiler could just serialize a decimal anyway. But attributes are part of a cross-language, runtime-level metadata system. C# does not get to invent new element types for custom attribute encoding without CLR support.

That distinction matters. The limitation belongs to the underlying metadata representation, not just to C# syntax rules.

Common Pitfalls

A common misconception is that const decimal should make decimal valid for attributes. It does not. Compile-time const-ness and valid attribute parameter types are different rules.

Another issue is using double as a quick replacement for financial data. That solves the compiler error but may introduce rounding behavior you explicitly wanted to avoid by choosing decimal in the first place.

Parsing strings inside attributes also needs care. Always use invariant formatting if the value is part of source code metadata. Otherwise, culture-specific parsing can become a hidden bug.

Finally, avoid overusing attributes for data that is not really metadata. If the value changes often or needs rich structure, a configuration file or regular code object may be the better design.

Summary

  • 'decimal is not a valid attribute parameter type because CLR custom attribute metadata does not support it.'
  • The restriction is about metadata encoding, not about whether decimal is a valid C# type.
  • Use double only when approximate numeric values are acceptable.
  • Use string or integer minor units when exact decimal semantics matter.
  • Attribute arguments are for simple metadata, so do not force complex data into them if a better design exists.

Course illustration
Course illustration

All Rights Reserved.