Skip to content

Commit

Permalink
Support for injection of ref and out arguments for methods
Browse files Browse the repository at this point in the history
  • Loading branch information
NikolayPianikov committed Sep 13, 2024
1 parent 09ac162 commit 2f7ef9e
Show file tree
Hide file tree
Showing 5 changed files with 216 additions and 8 deletions.
25 changes: 23 additions & 2 deletions src/Pure.DI.Core/Core/Code/BuildTools.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ namespace Pure.DI.Core.Code;
internal class BuildTools(
IFilter filter,
ITypeResolver typeResolver,
IBaseSymbolsProvider baseSymbolsProvider)
IBaseSymbolsProvider baseSymbolsProvider,
[Tag("Injection")] IIdGenerator idGenerator)
: IBuildTools
{
public void AddPureHeader(LinesBuilder code)
Expand All @@ -17,8 +18,28 @@ public void AddPureHeader(LinesBuilder code)

public string GetDeclaration(Variable variable, string separator = " ") =>
variable.IsDeclared ? "" : $"{typeResolver.Resolve(variable.Setup, variable.InstanceType)}{separator}";

public string OnInjected(BuildContext ctx, Variable variable)
{
var injection = OnInjectedInternal(ctx, variable);
var refKind = variable.RefKind switch
{
RefKind.Ref or RefKind.RefReadOnlyParameter => "ref",
RefKind.Out => "out",
_ => ""
};

if (!string.IsNullOrEmpty(refKind))
{
var localVarName = $"{variable.VariableDeclarationName}_{refKind}{idGenerator.Generate()}";
ctx.Code.AppendLine($"{variable.InstanceType} {localVarName} = {injection};");
injection = $"{refKind} {localVarName}";
}

return injection;
}

private string OnInjectedInternal(BuildContext ctx, Variable variable)
{
var variableCode = variable.VariableCode;
if (variableCode == variable.VariableName)
Expand Down
18 changes: 16 additions & 2 deletions src/Pure.DI.Core/Core/Code/ImplementationCodeBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,21 @@ private static void PropertyInjection(BuildContext ctx, DpProperty property, Var

private static void MethodInjection(BuildContext ctx, DpMethod method, ImmutableArray<Variable> methodArgs)
{
var args = string.Join(", ", methodArgs.Select(i => ctx.BuildTools.OnInjected(ctx, i)));
ctx.Code.AppendLine($"{ctx.Variable.VariableName}.{method.Method.Name}({args});");
var args = new List<string>();
for (var index = 0; index < methodArgs.Length; index++)
{
var variable = methodArgs[index];
if (index < method.Parameters.Length)
{
variable = variable with
{
RefKind = method.Parameters[index].ParameterSymbol.RefKind
};
}

args.Add(ctx.BuildTools.OnInjected(ctx, variable));
}

ctx.Code.AppendLine($"{ctx.Variable.VariableName}.{method.Method.Name}({string.Join(", ", args)});");
}
}
3 changes: 2 additions & 1 deletion src/Pure.DI.Core/Core/Code/Variable.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ internal record Variable(
VariableInfo Info,
bool IsLazy,
bool HasCycle,
string NameOverride = "")
string NameOverride = "",
RefKind RefKind = RefKind.None)
: IStatement
{
private string? _variableCode;
Expand Down
1 change: 1 addition & 0 deletions src/Pure.DI.Core/Generator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,7 @@ private void Setup() => DI.Setup()
.Bind().To<Filter>()
.Bind("UniqueTags").To<IdGenerator>()
.Bind("GenericType").To<IdGenerator>()
.Bind("Injection").To<IdGenerator>()
.Bind().To<IdGenerator>()
.Bind().To<Registry<TT>>();
}
177 changes: 174 additions & 3 deletions tests/Pure.DI.IntegrationTests/MethodInjectionTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ public class Program
public static void Main()
{
var composition = new Composition("dep");
var service = composition.Service;
var service = composition.Service;
}
}
}
Expand Down Expand Up @@ -173,7 +173,7 @@ public class Program
public static void Main()
{
var composition = new Composition("dep");
var service = composition.Service;
var service = composition.Service;
}
}
}
Expand Down Expand Up @@ -266,7 +266,7 @@ public class Program
public static void Main()
{
var composition = new Composition("dep");
var service = composition.Service;
var service = composition.Service;
}
}
}
Expand All @@ -276,4 +276,175 @@ public static void Main()
result.Success.ShouldBeTrue(result);
result.StdOut.ShouldBe(["Initialize dep", "Initialize dep", "Initialize", "True", "Activate"], result);
}

[Fact]
public async Task ShouldSupportMethodInjectionWhenRefParam()
{
// Given

// When
var result = await """
using System;
using Pure.DI;

namespace Sample
{
interface IDependency {}

class Dependency: IDependency
{
[Ordinal(1)]
internal void Initialize([Tag(374)] ref string depName)
{
Console.WriteLine($"Initialize {depName}");
}
}

interface IService
{
IDependency Dep { get; }
}

class Service: IService
{
private IDependency _dep;
public Service(IDependency dep)
{
_dep = dep;
}

public IDependency Dep => _dep;

public void Run()
{
Console.WriteLine("Run");
}

[Ordinal(7)]
public void Activate()
{
Console.WriteLine("Activate");
}

[Ordinal(1)]
internal void Initialize(IDependency dep)
{
Console.WriteLine("Initialize");
Console.WriteLine(dep != Dep);
}
}

static class Setup
{
private static void SetupComposition()
{
DI.Setup("Composition")
.Bind<IDependency>().To<Dependency>()
.Bind<IService>().To<Service>()
.Arg<string>("depName", 374)
.Root<IService>("Service");
}
}

public class Program
{
public static void Main()
{
var composition = new Composition("dep");
var service = composition.Service;
}
}
}
""".RunAsync();

// Then
result.Success.ShouldBeTrue(result);
result.StdOut.ShouldBe(["Initialize dep", "Initialize dep", "Initialize", "True", "Activate"], result);
}

[Fact]
public async Task ShouldSupportMethodInjectionWhenOutParam()
{
// Given

// When
var result = await """
using System;
using Pure.DI;

namespace Sample
{
interface IDependency {}

class Dependency: IDependency
{
[Ordinal(1)]
internal void Initialize([Tag(374)] out string depName)
{
depName = "";
Console.WriteLine($"Initialize");
}
}

interface IService
{
IDependency Dep { get; }
}

class Service: IService
{
private IDependency _dep;
public Service(IDependency dep)
{
_dep = dep;
}

public IDependency Dep => _dep;

public void Run()
{
Console.WriteLine("Run");
}

[Ordinal(7)]
public void Activate()
{
Console.WriteLine("Activate");
}

[Ordinal(1)]
internal void Initialize(IDependency dep)
{
Console.WriteLine("Initialize");
Console.WriteLine(dep != Dep);
}
}

static class Setup
{
private static void SetupComposition()
{
DI.Setup("Composition")
.Bind<IDependency>().To<Dependency>()
.Bind<IService>().To<Service>()
.Arg<string>("depName", 374)
.Root<IService>("Service");
}
}

public class Program
{
public static void Main()
{
var composition = new Composition("dep");
var service = composition.Service;
}
}
}
""".RunAsync();

// Then
result.Success.ShouldBeTrue(result);
result.StdOut.ShouldBe(["Initialize", "Initialize", "Initialize", "True", "Activate"], result);
}
}

0 comments on commit 2f7ef9e

Please sign in to comment.