Attributes
Writing attributes on declarations, declaring your own with attribute, their constant arguments, the attributes the compiler and the library give meaning to, and reading attributes with reflection.
On this page
An attribute is a note on a declaration, written in brackets before it: [Column("name", Width = 40)]. The compiler reads some attributes itself; the others are kept with the program, and reflection gives them back as objects at run time.
Writing attributes
[Serializable]
[Column("people", Width = 40)]
[Tag("a"), Tag("b")]
public class Person { … }
- Each attribute names a class derived from
System.Attribute, written without itsAttributesuffix:[Column]is the classColumnAttribute. The full name works too. - Several attributes go in one pair of brackets, separated by commas, or each in its own.
- The arguments in parentheses go to a constructor of the class. After them,
Name = valuesets a public field or a settable property. - Without arguments the parentheses can be left out:
[Serializable].
Attributes can be written on:
| Declaration | Example |
|---|---|
| Types: classes, values, records, interfaces, enums | [Tag("t")] public class C { … } |
| Enum members | enum E { [Tag("a")] A, B } |
| Fields | [Tag("f")] i32 F; |
| Properties | [Tag("p")] i32 P { get; set; } |
| Constructors | [Tag("c")] new() { } |
| Methods and free functions | [Tag("m")] M() { } |
| Parameters | M([Tag("x")] i32 x) { } |
Warning
Not yet available: attributes on a function's result ([return: NotNull]).
Constant arguments
Every argument is known when the program compiles:
- numbers,
bool, characters and strings, and constants made of them (N * 2for aconst i32 N); - enum members;
typeof(T), for a parameter or field of typeType;null, for a nullable parameter or field.
| Code | When |
|---|---|
| AN1058 | The name is not an attribute: no class Name or NameAttribute derived from System.Attribute. |
| AN1059 | The attribute class is abstract (a base class). |
| AN1060 | No constructor of the class takes these arguments. |
| AN1061 | Name = value names no public field or settable property. |
| AN1062 | The value given to Name = value is not a constant of its type. |
Declaring attributes
attribute Name { … } declares the class NameAttribute, derived from System.Attribute. The suffix is added when it is not written, and is not repeated when it is. Its base can be another attribute, written without the suffix:
import System;
public attribute Column
{
public:
str Name;
i32 Width;
bool Required;
new(str name) { Name = name; }
}
public attribute Note : Column
{
public:
new() : base("note") { }
}
[Column("people", Width = 40)]
public class Person
{
public:
[Column("full name", Required = true)] str Name = "";
[Note] i32 Age;
}
Main()
{
}
An attribute class is a class like any other: fields, properties, constructors and methods. The base of an attribute must derive from System.Attribute (AN1096). The constraint T : attribute asks a type argument to be an attribute class (see Generics).
Reading attributes
System.Reflection gives each declaration's attributes as objects, made with the arguments written:
| Member | On | What it gives |
|---|---|---|
GetCustomAttributes() |
Type, FieldInfo, PropertyInfo, MethodInfo, ConstructorInfo, ParameterInfo |
Every attribute, as an any[]. |
GetCustomAttributes(typeof(A)) |
the same | Those of class A or a class derived from it. |
IsDefined(typeof(A)) |
the same | Whether there is one. |
import System;
import System.Reflection;
public attribute Column
{
public:
str Name;
i32 Width;
bool Required;
new(str name) { Name = name; }
}
public attribute Note : Column
{
public:
new() : base("note") { }
}
[Column("people", Width = 40)]
public class Person
{
public:
[Column("full name", Required = true)] str Name = "";
[Note] i32 Age;
[Column("greet")]
Greet([Column("who")] str other) : str => "hi " + other;
}
Main()
{
Type t = typeof(Person);
loop (any a in t.GetCustomAttributes())
{
if (a is ColumnAttribute c)
Console.WriteLine("type: " + c.Name + " " + c.Width);
}
FieldInfo? name = t.GetField("Name");
if (name is FieldInfo f && f.GetCustomAttributes(typeof(ColumnAttribute))[0] is ColumnAttribute col)
Console.WriteLine("field: " + col.Name + " " + col.Required);
if (t.GetField("Age") is FieldInfo age)
Console.WriteLine("Age has Note: " + age.IsDefined(typeof(NoteAttribute)));
MethodInfo? greet = t.GetMethod("Greet");
if (greet is MethodInfo m)
{
ParameterInfo p = m.GetParameters()[0];
if (p.GetCustomAttributes()[0] is ColumnAttribute pc)
Console.WriteLine("parameter: " + pc.Name);
}
Console.WriteLine(t.IsDefined(typeof(NoteAttribute)));
}
larva run
type: people 40
field: full name true
Age has Note: true
parameter: who
false
Enum members appear among their type's fields (GetFields()), with their attributes.
Attributes the compiler reads
These are read where they apply and are not kept for reflection. The compiler knows them by name, so they need no import.
| Attribute | On | What it does |
|---|---|---|
[Inline] |
functions and methods | Asks the back end to inline every call. |
[NoInline] |
functions and methods | Asks the back end never to inline it. |
[Import("lib")], [Import("lib", "Entry")] |
functions without a body | A C function of a native library (see Interop). |
[Export], [Export("name")] |
static functions | Makes the function a C symbol (see Interop). |
[Primitive("i32")] |
value types of the core library |
Says which primitive type a library type stands for (System.Int32 is i32). Only the core library uses it (AN1003, AN1004). |
Attributes of the library
| Attribute | Namespace | What it does |
|---|---|---|
[FriendOf(T)] |
System |
Makes the type and T friends (both ways, from any library): each sees the other's friendly members. It names one type (AN1070), not the type itself (AN1071). |
[ValueLayout(Layout.Sequential or Layout.Explicit, Pack = n, Size = n)] |
System.Runtime.InteropServices |
Where the fields of a value go in memory (see Interop). |
[FieldOffset(n)] |
System.Runtime.InteropServices |
A field's offset in a Layout.Explicit value. |
import System;
[FriendOf(Vault)]
public class Auditor
{
public:
Peek(Vault v) : i32 => v.secret;
}
public class Vault
{
friendly:
i32 secret = 42;
}
Main()
{
Console.WriteLine(new Auditor().Peek(new Vault()));
Console.WriteLine(typeof(Vault).IsFriendOf(typeof(Auditor)));
}
larva run
42
true
Another type that reads v.secret gets AN1401: `secret` is friendly: only for `Vault` and its friends. See Access.
Edit this page on GitHub Report an issue