Pure.DI can generate the standard Func<ReadOnlySpan<char>, T> factory automatically. The runtime span argument is kept as a local value inside the generated delegate invocation, so no manual ctx.Override(...) call and no lock (ctx.Lock) block are required.
using Shouldly;
using Pure.DI;
using System;
DI.Setup(nameof(Composition))
// Composition root
.Root<Func<ReadOnlySpan<char>, Parser>>("ParserFactory");
var composition = new Composition();
var parser = composition.ParserFactory("Hello".AsSpan());
parser.Value.ShouldBe("Hello");
class Parser
{
private string _value = "";
[Ordinal]
public void Initialize(ReadOnlySpan<char> text)
{
_value = text.ToString();
}
public string Value => _value;
}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 runUse this default Func binding when the standard delegate shape is enough. Use a custom delegate factory with explicit ctx.Override<T>(...) only when you need a custom delegate type or additional factory logic.
This is the form to reach for when a hot path receives text as a span and must build an object from it without allocating an intermediate string: the span lives only inside the delegate call, and Parser.Initialize copies it once into a field.
Because the span never escapes the invocation, the generated code needs no shared override state and therefore no lock. That is what makes the default binding cheaper and safer than a hand-written factory.
The following partial class will be generated
partial class Composition
{
public Func<ReadOnlySpan<char>, Parser> ParserFactory
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
Func<ReadOnlySpan<char>, Parser> perBlockFuncReadOnlySpanCharParser;
// Creates a factory with runtime arguments
Func<ReadOnlySpan<char>, Parser> localFactory = new Func<ReadOnlySpan<char>, Parser>((ReadOnlySpan<char> localArg1) =>
{
// Creates the result
ReadOnlySpan<char> overriddenReadOnlySpanChar = localArg1;
var transientParser = new Parser();
transientParser.Initialize(overriddenReadOnlySpanChar);
return transientParser;
});
perBlockFuncReadOnlySpanCharParser = localFactory;
return perBlockFuncReadOnlySpanCharParser;
}
}
}Class diagram:
---
config:
class:
hideEmptyMembersBox: true
---
classDiagram
Composition ..> FuncᐸReadOnlySpanᐸCharᐳˏParserᐳ : FuncᐸReadOnlySpanᐸCharᐳˏParserᐳ ParserFactory
FuncᐸReadOnlySpanᐸCharᐳˏParserᐳ *-- Parser : Parser
Parser *-- ReadOnlySpanᐸCharᐳ : ReadOnlySpanᐸCharᐳ
namespace Pure.DI.UsageTests.BCL.DefaultFuncWithReadOnlySpanScenario {
class Composition {
<<partial>>
+FuncᐸReadOnlySpanᐸCharᐳˏParserᐳ ParserFactory
}
class Parser {
<<class>>
+Parser()
+Initialize(ReadOnlySpanᐸCharᐳ text) : Void
}
}
namespace System {
class FuncᐸReadOnlySpanᐸCharᐳˏParserᐳ {
<<delegate>>
}
class ReadOnlySpanᐸCharᐳ {
<<struct>>
}
}
See also: