An attribute attaches information to a declaration (C# specification clause 23). The compiler stores it in the assembly’s metadata, where tools and the program itself can read it through reflection. A few attributes change what the compiler does.
Using attributes
[Serializable]
public class Order { }
[Obsolete("Use Add instead", error: true)]
public void Insert() { }
[Conditional("DEBUG"), Description("Logs")] // two attributes in one section
public void Log() { }
[return: NotNull]
public string Name() => "";
An attribute section is written in square brackets before a declaration, and may list
several attributes. The Attribute suffix may be left out of the name, so
[Serializable] means SerializableAttribute. If both forms exist
and are both attributes, the short name is ambiguous; a leading @ selects
the exact name, as in [@Serializable]. Arguments are in parentheses:
positional first, then named.
Targets
A target states what an attribute applies to, when the position is unclear.
| Target | Applies to |
|---|---|
assembly | The whole assembly; written after usings, before any member |
module | The current module, in the same position |
type | A class, struct, interface, enum or delegate |
method | A method, an accessor, an operator, a constructor or a finalizer |
property | A property or indexer |
field | A field; on an auto-property, its backing field (C# 7.3) |
event | An event |
param | A parameter |
return | A return value |
[assembly: AssemblyVersion("1.0.0.0")]
public record Person([property: JsonPropertyName("n")] string Name);
[field: NonSerialized]
public int Cached { get; set; }
Attributes can also be placed on lambda expressions (C# 10) and local functions (C# 9).
Declaring an attribute
An attribute is a class derived from System.Attribute (clause 23.2).
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true, Inherited = false)]
public sealed class AuthorAttribute : Attribute
{
public AuthorAttribute(string name) => Name = name; // positional parameter
public string Name { get; }
public int Version { get; set; } // named parameter
}
[Author("Ann", Version = 2)]
class Report { }
- Constructor parameters are positional parameters. Public read-write fields and properties are named parameters.
- Argument values must be constant expressions,
typeofexpressions or single-dimensional arrays of these (clause 23.2.4). - The usable parameter types are
bool,byte,char,double,float,int,long,sbyte,short,string,uint,ulong,ushort,object,System.Type, enums, and one-dimensional arrays of these. - Since C# 11 an attribute class may be generic, used as
[Author<int>]. The type arguments must be fully known at compile time.
The default AttributeUsage allows any target, once, and is inherited. At
run time, GetCustomAttributes and related reflection methods read them.
Attributes the compiler recognises
| Attribute | Effect | Clause |
|---|---|---|
AttributeUsage | Declares what an attribute may apply to | 23.5.2 |
Conditional("SYMBOL") | Calls to the method, which must return void, are removed unless the symbol is defined. Also on attribute classes | 23.5.3 |
Obsolete | Gives a warning, or an error with error: true | 23.5.4 |
AsyncMethodBuilder | Names the builder for a task-like type; on a method since C# 10 | 23.5.5 |
CallerLineNumber, CallerFilePath, CallerMemberName | Optional parameters filled with the call site’s details (C# 5) | 23.5.6 |
CallerArgumentExpression("param") | Fills in the source text of the argument given for another parameter (C# 10) | 23.5.6 |
UnscopedRef | Lifts a scoped restriction | 23.5.8 |
EnumeratorCancellation | Marks the token parameter of an async iterator | 23.5.10 |
ModuleInitializer | Runs a parameterless static void method when the module loads (C# 9) | 23.5.11 |
SetsRequiredMembers | Says that a constructor sets all required members | 23.5.12 |
CollectionBuilder | Names the builder for collection expressions | 23.5.13 |
InlineArray(n) | Marks an inline array | 23.5.14 |
OverloadResolutionPriority | Prefers an overload (C# 13) | 23.5.15 |
Experimental | Marks an API as experimental: using it is an error unless suppressed (C# 12) | none |
SkipLocalsInit | Omits the zeroing of locals (C# 9; needs unsafe code) | none |
Interoperation attributes such as DllImport, StructLayout and
FieldOffset are described in clause 23.6.
Code analysis attributes
These attributes in System.Diagnostics.CodeAnalysis tell the compiler’s
nullable analysis what a method
does (clause 23.5.7).
| Attribute | Meaning |
|---|---|
AllowNull | Null is allowed as an input, even though the type is non-nullable |
DisallowNull | Null is not allowed as an input, even though the type is nullable |
MaybeNull | The output may be null, even though the type is non-nullable |
NotNull | The output will not be null, even though the type is nullable |
MaybeNullWhen(bool) | A parameter may be null when the method returns that value |
NotNullWhen(bool) | A parameter will not be null when the method returns that value |
NotNullIfNotNull(name) | The result is not null if the named argument is not null |
DoesNotReturn | The method never returns normally |
DoesNotReturnIf(bool) | The method does not return if the argument has that value |
MemberNotNull(names) | The named members are not null when the method returns |
MemberNotNullWhen(bool, names) | The named members are not null when the method returns that value |