Service registration
Tharga.Runtime adds filter-based extension methods on IServiceCollection. They scan the relevant assemblies, find every type matching the filter, and register each one against the interfaces it directly implements.
The four entry points
using Tharga.Runtime;
services.AddTransient(x => x.IsOfType<IHandler>());
services.AddScoped (x => x.IsOfType<IHandler>());
services.AddSingleton(x => x.IsOfType<IHandler>());
services.Add(RegistrationType.Singleton, x => x.IsOfType<IHandler>());
Each one accepts a Func<TypeInfo, bool> filter and adds one DI registration per (ServiceType, ImplementationType) pair it finds. Interfaces and abstract types are filtered out as implementations automatically — only concrete classes get registered.
Generic overloads
If the base type is always the same, pass it as the type parameter instead of repeating it in the filter:
services.AddTransient<IHandler>(x => !x.IsAbstract);
The generic overload composes IsOfType<IHandler>() into the filter for you.
findInterface
Default is true — implementations are registered against their directly implemented interfaces:
public interface IHandler { }
public class OrderHandler : IHandler { }
services.AddTransient<IHandler>(x => true);
// Resolves: IHandler → OrderHandler
Pass findInterface: false to register concrete types as themselves:
services.AddTransient<IHandler>(x => true, findInterface: false);
// Resolves: OrderHandler → OrderHandler
"Directly implemented" means the interface is declared on the type itself, not inherited via another interface — see IsInterfaceDirectlyImplemented.
Assembly scope
The optional assemblies and baseAssembly parameters work the same way as on IAssemblyService — default is the entry assembly plus same-namespace-root assemblies in the app domain. Pass an explicit list when you need a different scope:
services.AddTransient<IHandler>(
filter: x => !x.IsAbstract,
assemblies: [typeof(OrderHandler).Assembly, typeof(InvoiceHandler).Assembly]);
GetServiceTypePairs — inspect before registering
If you want to see what would be registered (for logging, diagnostics, or custom registration logic), GetServiceTypePairs returns the pairs without touching the container:
var pairs = ServiceCollectionExtensions
.GetServiceTypePairs<IHandler>(x => !x.IsAbstract);
foreach (var (serviceType, implementationType) in pairs)
{
Console.WriteLine($"{serviceType.Name} → {implementationType.Name}");
}
RegistrationType
public enum RegistrationType
{
Transient,
Scoped,
Singleton
}
Passed to the non-generic Add(...) overload when the lifetime is computed at runtime.