tag-on-a-constructor-argument.md

July 3, 2026 · View on GitHub

Tag on a constructor argument

Tag.OnConstructorArg<T>(name) creates a tag that targets the constructor parameter name of type T, letting you choose a specific implementation for a single injection site without adding [Tag(...)] attributes to the consuming class — useful when you can't or don't want to modify its code. The wildcards * and ? are supported.

using Shouldly;
using Pure.DI;

DI.Setup(nameof(Composition))
    .Bind(Tag.OnConstructorArg<DataReplicator>("sourceStream"))
        .To<FileStream>()
    .Bind(Tag.OnConstructorArg<StreamProcessor<TT>>("stream"))
        .To<NetworkStream>()
    .Bind<IDataReplicator>().To<DataReplicator>()

    // Specifies to create the composition root named "Root"
    .Root<IDataReplicator>("Replicator");

var composition = new Composition();
var replicator = composition.Replicator;
replicator.SourceStream.ShouldBeOfType<FileStream>();
replicator.TargetStream.ShouldBeOfType<NetworkStream>();

interface IStream;

class FileStream : IStream;

class NetworkStream : IStream;

class StreamProcessor<T>(IStream stream)
{
    public IStream Stream { get; } = stream;
}

interface IDataReplicator
{
    IStream SourceStream { get; }

    IStream TargetStream { get; }
}

class DataReplicator(
    IStream sourceStream,
    StreamProcessor<string> processor)
    : IDataReplicator
{
    public IStream SourceStream { get; } = sourceStream;

    public IStream TargetStream => processor.Stream;
}
Running this code sample locally
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

Warning

Each potentially injectable argument, property, or field contains an additional tag. This tag can be used to specify what can be injected there. This will only work if the binding type and the tag match. So while this approach can be useful for specifying what to enter, it can be more expensive to maintain and less reliable, so it is recommended to use attributes like [Tag(...)] instead.

The following partial class will be generated
partial class Composition
{
  public IDataReplicator Replicator
  {
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    get
    {
      return new DataReplicator(new FileStream(), new StreamProcessor<string>(new NetworkStream()));
    }
  }
}

Class diagram:

---
 config:
  class:
   hideEmptyMembersBox: true
---
classDiagram
	FileStream --|> IStream
	NetworkStream --|> IStream
	DataReplicator --|> IDataReplicator
	Composition ..> DataReplicator : IDataReplicator Replicator
	DataReplicator *-- FileStream : IStream
	DataReplicator *-- StreamProcessorᐸStringᐳ : StreamProcessorᐸStringᐳ
	StreamProcessorᐸStringᐳ *-- NetworkStream : IStream
	namespace Pure.DI.UsageTests.Advanced.TagOnConstructorArgScenario {
		class Composition {
		<<partial>>
		+IDataReplicator Replicator
		}
		class DataReplicator {
				<<class>>
			+DataReplicator(IStream sourceStream, StreamProcessorᐸStringᐳ processor)
		}
		class FileStream {
				<<class>>
			+FileStream()
		}
		class IDataReplicator {
			<<interface>>
		}
		class IStream {
			<<interface>>
		}
		class NetworkStream {
				<<class>>
			+NetworkStream()
		}
		class StreamProcessorᐸStringᐳ {
				<<class>>
			+StreamProcessor(IStream stream)
		}
	}

See also: