2 Commits

Author SHA1 Message Date
Samuele Lorefice
65f624a355 Exposes GetService(Type, obiect[]? args) and it's safer variant TryGetService() to enable consumers to request a service without needing to do reflection work themselves.
All checks were successful
Nuget Pkg Build / build (push) Successful in 1m11s
bumps version to 0.0.1.8-alpha
2025-10-01 19:03:53 +02:00
Samuele Lorefice
66e7fcc798 Fixes #5 makes copy of the args list instead of stripping it away from the descriptor (preventing catastrophic problems). Bumps version
All checks were successful
Nuget Pkg Build / build (push) Successful in 1m7s
2025-09-24 19:53:29 +02:00
3 changed files with 45 additions and 21 deletions

View File

@@ -24,7 +24,7 @@ public class ServiceContainer {
/// <typeparam name="TService"></typeparam>
public List<TService> GetServices<TService>() where TService : class =>
descriptors.Where(d => d.ServiceType == typeof(TService))
.Select(d => (TService)GetService(d.ImplementationType)).ToList();
.Select(d => (TService)GetService(d.ImplementationType, d.Arguments?.ToArray())).ToList();
/// <summary>
/// Registers a singleton service with its implementation.
@@ -153,26 +153,42 @@ public class ServiceContainer {
});
return this;
}
/// <summary>
/// Resolves and returns an instance of the requested service type.
/// </summary>
/// <param name="serviceType">Type of the service that's being requested</param>
/// <param name="args">arguments to pass to the constructor of the service</param>
/// <remarks> you can't call generic methods with an unknown type at compile time
/// so we use reflection to call the generic GetService{T} method with the provided
/// type Basically we build the method GetService{serviceType}() at runtime and then call it.</remarks>
/// <returns>An object that is the instantiated service type</returns>
public object GetService(Type serviceType, object[]? args = null) {
List<Type> arguments = [serviceType];
// you can't call generic methods with an unknown type at compile time
// so we use reflection to call the generic GetService<T> method with the provided type
// Basically we build the method GetService<serviceType>() at runtime and then call it.
// "Classic black magic sorcery" in reflection.
private object GetService(Type serviceType) {
if (args != null) arguments.AddRange(args.ToList().Select(a => a.GetType()));
var method = typeof(ServiceContainer)
.GetMethod(nameof(GetService))!
.MakeGenericMethod(serviceType);
return method.Invoke(this, null)!;
.MakeGenericMethod(arguments.ToArray());
return method.Invoke(this, args)!;
}
private object? TryGetService(Type serviceType) {
var method = typeof(ServiceContainer)
.GetMethod(nameof(GetService))!
.MakeGenericMethod(serviceType);
/// <summary>
/// tries to resolve and return an instance of the requested service type. Returns null if it fails.
/// </summary>
/// <param name="serviceType">Type of the service that's being requested</param>
/// <param name="args">arguments to pass to the constructor of the service</param>
/// <remarks> you can't call generic methods with an unknown type at compile time
/// so we use reflection to call the generic GetService{T} method with the provided
/// type Basically we build the method GetService{serviceType}() at runtime and then call it.</remarks>
/// <returns>An object that is the instantiated service type or null if not found</returns>
public object? TryGetService(Type serviceType, object[]? args = null) {
try {
return method.Invoke(this, null)!;
return GetService(serviceType, args);
} catch {
return null!;
return null;
}
}
@@ -224,13 +240,13 @@ public class ServiceContainer {
singletons[descriptor.ServiceType] = newSingleton!;
return newSingleton;
}
private TInterface Instantiate<TInterface>(ServiceDescriptor descriptor, ConstructorInfo? ctor = null) {
if (ctor == null && descriptor.ImplementationType.GetConstructors().Length > 1)
throw new Exception($"Multiple constructors found for type {descriptor.ImplementationType}. Please provide a specific constructor.");
List<ParameterInfo> par;
List<object> args = descriptor.Arguments ?? new List<object>();
List<object> args = descriptor.Arguments != null ? new List<object>(descriptor.Arguments) : new List<object>();
if (ctor == null)
par = descriptor.ImplementationType

View File

@@ -8,20 +8,28 @@ public class ServiceDescriptor
/// <summary>
/// Gets or sets the type of the service to be provided.
/// </summary>
public required Type ServiceType { get; set; }
public required Type ServiceType { get; init; }
/// <summary>
/// Gets or sets the concrete type that implements the service.
/// </summary>
public required Type ImplementationType { get; set; }
public required Type ImplementationType { get; init; }
/// <summary>
/// Gets or sets the lifetime of the service (e.g., Singleton or Transient).
/// </summary>
public required ServiceLifetime Lifetime { get; set; }
public required ServiceLifetime Lifetime { get; init; }
/// <summary>
/// Arguments to be passed to the constructor of the implementation type.
/// </summary>
public List<object>? Arguments { get; set; }
public List<object>? Arguments { get; init; }
/// <summary>
/// Returns a string with the specific type of service, its implementation, and its lifetime.
/// </summary>
/// <returns>{implementation Name} as {Service Name} ({Lifetime})</returns>
public override string ToString() {
return $"{ImplementationType.Name} as {ServiceType.Name} ({Lifetime})";
}
}

View File

@@ -9,7 +9,7 @@
<LangVersion>latest</LangVersion>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<Version>0.0.1.6-alpha</Version>
<Version>0.0.1.8-alpha</Version>
<Title>Syrette </Title>
<Authors>Lorefice Samuele</Authors>
<Description>Syrette is a minimalistic dependency injection library for C#. It aims to provide a simple and efficient way to achieve dependency injections in your applications without the overhead of larger frameworks.</Description>