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 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 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: