Instance Generator
On this page
InstanceGenerator (analyzers/Nalix.Analyzers.Generators/InstanceGenerator.cs) is an incremental Roslyn source generator that emits compile-time constructor activation delegates and service mappings for [Injectable] classes and SingletonBase<T> subclasses.
Source Mapping
analyzers/Nalix.Analyzers.Generators/InstanceGenerator.csanalyzers/Nalix.Analyzers.Generators/Internal/GeneratorDiagnosticDescriptors.cs
What It Does at Compile Time
- Discovers
[Injectable]Services andSingletonBase<T>Subclasses:- Scans every concrete class annotated with
[Injectable]or[Injectable(typeof(TService))]. - Scans every concrete class inheriting from
SingletonBase<T>.
- Scans every concrete class annotated with
- Validates & Compiles Accessible Constructors:
- For
[Injectable]classes:- Collects all
publicandinternalinstance constructors. If none exist, reports errorNALIX063. - Partitioning constructor parameters into
ConfigurationLoaderparameters (resolved automatically fromConfigurationManager.Instance.Get<TConfig>()) and caller-supplied runtimeobject?[] args, checks whether two constructors have the same non-config parameter count (args.Length) and overlapping parameter types. If so, reports errorNALIX064. - Emits a
static args => switch (args.Length)factory delegate supporting all accessible constructors.
- Collects all
- For
SingletonBase<T>classes:- Requires a
publicorinternalparameterless constructor (protectedalone is not accessible to the generated initializer); otherwise reports warningNALIX065.
- Requires a
- For
- Emits
[ModuleInitializer]Registration (InstanceGenerated.Initialize()):- Calls
InstanceManager.RegisterActivator<TImpl>(static args => ...) - Calls
InstanceManager.RegisterServiceMapping<TService, TImpl>()for each[Injectable(typeof(TService))]attribute - Calls
SingletonActivatorCache.Register<TSingleton>(static () => new TSingleton())forSingletonBase<T>subclasses
- Calls
Example
using Microsoft.Extensions.Logging;
using Nalix.Abstractions.Injection;
using Nalix.Network.Options;
[Injectable(typeof(IMatchmakerService))]
public sealed class MatchmakerService : IMatchmakerService
{
private readonly NetworkSocketOptions _socketOptions;
private readonly ILogger? _logger;
public MatchmakerService(NetworkSocketOptions socketOptions)
: this(socketOptions, null)
{
}
public MatchmakerService(NetworkSocketOptions socketOptions, ILogger? logger)
{
_socketOptions = socketOptions;
_logger = logger;
}
}
Generated initializer in InstanceGenerated.g.cs:
[ModuleInitializer]
internal static void Initialize()
{
InstanceManager.RegisterActivator<MatchmakerService>(static args =>
{
return args.Length switch
{
0 => new MatchmakerService(
ConfigurationManager.Instance.Get<NetworkSocketOptions>()),
1 => new MatchmakerService(
ConfigurationManager.Instance.Get<NetworkSocketOptions>(),
(ILogger)args[0]!),
_ => throw new InvalidOperationException(
$"No constructor on 'MatchmakerService' matches {args.Length} argument(s).")
};
});
InstanceManager.RegisterServiceMapping<IMatchmakerService, MatchmakerService>();
}
Diagnostics Emitted
| ID | Title | Severity | Cause |
|---|---|---|---|
NALIX063 | No accessible constructor found | Error | [Injectable] class has no public or internal constructor. |
NALIX064 | Ambiguous constructors in injectable class | Error | [Injectable] class has multiple public/internal constructors with the same non-config parameter count and non-disjoint parameter types. |
NALIX065 | SingletonBase subclass missing accessible parameterless constructor | Warning | SingletonBase<T> subclass has no public or internal parameterless constructor. |