To use custom attributes, derive from System.Attribute and register them with the setup API:
TagAttributeOrdinalAttributeTypeAttributeYou can also use combined attributes. Each registration method can take an optional argument index (default is 0) that specifies where to read tag, ordinal, or type metadata. Use this when a codebase must not reference Pure.DI types in its domain code, or when the built-in names clash with another framework's attributes.
using Shouldly;
using Pure.DI;
DI.Setup(nameof(PersonComposition))
.TagAttribute<MyTagAttribute>()
.OrdinalAttribute<MyOrdinalAttribute>()
.TypeAttribute<MyTypeAttribute>()
.TypeAttribute<MyGenericTypeAttribute<TT>>()
.Arg<int>("personId")
.Bind().To(() => new Uri("https://github.com/DevTeam/Pure.DI"))
.Bind("NikName").To(() => "Nik")
.Bind().To<Person>()
// Composition root
.Root<IPerson>("Person");
var composition = new PersonComposition(personId: 123);
var person = composition.Person;
person.ToString().ShouldBe("123 Nik https://github.com/DevTeam/Pure.DI");
[AttributeUsage(
AttributeTargets.Constructor
| AttributeTargets.Method |
AttributeTargets.Property |
AttributeTargets.Field)]
class MyOrdinalAttribute(int ordinal) : Attribute;
[AttributeUsage(
AttributeTargets.Parameter
| AttributeTargets.Property
| AttributeTargets.Field)]
class MyTagAttribute(object tag) : Attribute;
[AttributeUsage(
AttributeTargets.Parameter
| AttributeTargets.Property
| AttributeTargets.Field)]
class MyTypeAttribute(Type type) : Attribute;
[AttributeUsage(
AttributeTargets.Parameter
| AttributeTargets.Property
| AttributeTargets.Field)]
class MyGenericTypeAttribute<T> : Attribute;
interface IPerson;
class Person([MyTag("NikName")] string name) : IPerson
{
private object? _state;
[MyOrdinal(1)] [MyType(typeof(int))] internal object Id = "";
[MyOrdinal(2)]
public void Initialize([MyGenericType<Uri>] object state) =>
_state = state;
public override string ToString() => $"{Id} {name} {_state}";
}Running this code sample locally
- Make sure you have the .NET SDK 10.0 or later installed
dotnet --list-sdk- Create a net10.0 (or later) console application
dotnet new console -n Sampledotnet add package Pure.DI
dotnet add package Shouldly- Copy the example code into the Program.cs file
You are ready to run the example 🚀
dotnet runNote
Custom attributes provide extensibility for advanced scenarios where standard attributes don't meet specific requirements.
The registration is what makes an attribute meaningful: without .TagAttribute<T>(), .OrdinalAttribute<T>() or .TypeAttribute<T>() the generator never reads it, and the members simply do not participate. Register the attributes that were actually declared in that assembly.
The registration is per composition, so a library that declares its own attributes also ships the registrations that make them work. An attribute that is registered in one composition but not in another produces different graphs from the same code.
Tip
Prefer one attribute class that carries tag and ordinal together (see custom universal attribute) over three narrowly scoped ones when the same member often needs both.
The following partial class will be generated
partial class PersonComposition
{
private readonly int _argPersonId;
[OrdinalAttribute(128)]
public PersonComposition(int personId)
{
_argPersonId = personId;
}
public IPerson Person
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
string transientString = "Nik";
Uri transientUri = new Uri("https://github.com/DevTeam/Pure.DI");
var transientPerson = new Person(transientString);
transientPerson.Id = _argPersonId;
transientPerson.Initialize(transientUri);
return transientPerson;
}
}
}Class diagram:
---
config:
class:
hideEmptyMembersBox: true
---
classDiagram
Uri --|> IEquatableᐸUriᐳ
Uri --|> ISpanFormattable
Uri --|> ISerializable
Person --|> IPerson
PersonComposition ..> Person : IPerson Person
Person o-- Int32 : Argument "personId"
Person *-- Uri : Uri
Person *-- String : "NikName" String
namespace Pure.DI.UsageTests.Attributes.CustomAttributesScenario {
class IPerson {
<<interface>>
}
class Person {
<<class>>
+Person(String name)
~Object Id
+Initialize(Object state) : Void
}
class PersonComposition {
<<partial>>
+IPerson Person
}
}
namespace System {
class IEquatableᐸUriᐳ {
<<interface>>
}
class Int32 {
<<struct>>
}
class ISpanFormattable {
<<interface>>
}
class String {
<<class>>
}
class Uri {
<<class>>
}
}
namespace System.Runtime.Serialization {
class ISerializable {
<<interface>>
}
}
See also: