custom-generic-argument-attribute.md
July 3, 2026 ¡ View on GitHub
Custom generic argument attribute
Besides the built-in TT marker types, you can define your own generic type argument markers. Register a custom attribute with GenericTypeArgumentAttribute<T>(), apply it to a marker type like TMy, and use that marker in bindings: a single Bind<IRepository<TMy>>().To<Repository<TMy>>() then resolves IRepository<T> for any T, such as IRepository<Post> and IRepository<Comment>.
using Shouldly;
using Pure.DI;
DI.Setup(nameof(Composition))
// Registers custom generic argument
.GenericTypeArgumentAttribute<GenericArgAttribute>()
.Bind<IRepository<TMy>>().To<Repository<TMy>>()
.Bind<IContentService>().To<ContentService>()
// Composition root
.Root<IContentService>("ContentService");
var composition = new Composition();
var service = composition.ContentService;
service.Posts.ShouldBeOfType<Repository<Post>>();
service.Comments.ShouldBeOfType<Repository<Comment>>();
[AttributeUsage(AttributeTargets.Interface | AttributeTargets.Class | AttributeTargets.Struct)]
class GenericArgAttribute : Attribute;
[GenericArg]
interface TMy;
interface IRepository<T>;
class Repository<T> : IRepository<T>;
class Post;
class Comment;
interface IContentService
{
IRepository<Post> Posts { get; }
IRepository<Comment> Comments { get; }
}
class ContentService(
IRepository<Post> posts,
IRepository<Comment> comments)
: IContentService
{
public IRepository<Post> Posts { get; } = posts;
public IRepository<Comment> Comments { get; } = comments;
}
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 Sample
dotnet 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 run
Note
Custom generic argument attributes are useful when you need to pass metadata specific to generic type parameters during binding resolution.
The following partial class will be generated
partial class Composition
{
public IContentService ContentService
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
return new ContentService(new Repository<Post>(), new Repository<Comment>());
}
}
}
Class diagram:
---
config:
class:
hideEmptyMembersBox: true
---
classDiagram
ContentService --|> IContentService
Repositoryá¸Postáł --|> IRepositoryá¸Postáł
Repositoryá¸Commentáł --|> IRepositoryá¸Commentáł
Composition ..> ContentService : IContentService ContentService
ContentService *-- Repositoryá¸Postáł : IRepositoryá¸Postáł
ContentService *-- Repositoryá¸Commentáł : IRepositoryá¸Commentáł
namespace Pure.DI.UsageTests.Attributes.CustomGenericArgumentAttributeScenario {
class Composition {
<<partial>>
+IContentService ContentService
}
class ContentService {
<<class>>
+ContentService(IRepositoryá¸Postáł posts, IRepositoryá¸Commentáł comments)
}
class IContentService {
<<interface>>
}
class IRepositoryá¸Commentáł {
<<interface>>
}
class IRepositoryá¸Postáł {
<<interface>>
}
class Repositoryá¸Commentáł {
<<class>>
+Repository()
}
class Repositoryá¸Postáł {
<<class>>
+Repository()
}
}
See also: