Skip to content

Commit ae8303f

Browse files
committed
Added DI Container example
1 parent 2b84a33 commit ae8303f

File tree

8 files changed

+169
-0
lines changed

8 files changed

+169
-0
lines changed

Diff for: DIContainer/Calculator.cs

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
namespace DIContainer;
2+
3+
public interface ICalculator
4+
{
5+
int Add(int a, int b);
6+
}
7+
8+
public class Calculator : ICalculator
9+
{
10+
public int Add(int a, int b) => a + b;
11+
}

Diff for: DIContainer/Container.cs

+78
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
using System.Reflection;
2+
3+
namespace DIContainer;
4+
5+
public class Container
6+
{
7+
private readonly Dictionary<Type, Type> _registeredTypes = new();
8+
private readonly Dictionary<Type, object?> _singletons = new();
9+
10+
public void Register<TInterface, TImplementation>() where TImplementation : TInterface
11+
{
12+
_registeredTypes[typeof(TInterface)] = typeof(TImplementation);
13+
}
14+
15+
public void RegisterSingleton<TInterface, TImplementation>() where TImplementation : TInterface
16+
{
17+
Register<TInterface, TImplementation>();
18+
19+
// add the type as singleton
20+
_singletons[typeof(TInterface)] = null;
21+
}
22+
23+
public TInterface Resolve<TInterface>()
24+
{
25+
return (TInterface)Resolve(typeof(TInterface));
26+
}
27+
28+
private object Resolve(Type type)
29+
{
30+
if (_registeredTypes.ContainsKey(type))
31+
{
32+
// Check if we already have a singleton registered and we created an instance
33+
if (_singletons.TryGetValue(type, out var value) && value is not null)
34+
return _singletons[type];
35+
36+
var implementationType = _registeredTypes[type];
37+
var constructor = implementationType.GetConstructors().First();
38+
var constructorParameters = constructor.GetParameters();
39+
object? instance = null;
40+
41+
if (constructorParameters.Length == 0)
42+
{
43+
// If the constructor has no parameters, we can just create an instance
44+
instance = Activator.CreateInstance(implementationType);
45+
}
46+
else
47+
{
48+
var parameterInstances = GetConstructorParameters(constructorParameters);
49+
instance = Activator.CreateInstance(implementationType, parameterInstances.ToArray());
50+
}
51+
52+
// If we are a singleton, add this to the dictionary
53+
TryAddWhenSingleton(type, instance);
54+
return instance;
55+
}
56+
57+
throw new Exception($"The type {type.FullName} has not been registered");
58+
}
59+
60+
private List<object> GetConstructorParameters(ParameterInfo[] constructorParameters)
61+
{
62+
var parameterInstances = new List<object>();
63+
foreach (var parameter in constructorParameters)
64+
{
65+
var parameterType = parameter.ParameterType;
66+
var parameterInstance = Resolve(parameterType);
67+
parameterInstances.Add(parameterInstance);
68+
}
69+
70+
return parameterInstances;
71+
}
72+
73+
private void TryAddWhenSingleton(Type type, object instance)
74+
{
75+
if (_singletons.ContainsKey(type))
76+
_singletons[type] = instance;
77+
}
78+
}

Diff for: DIContainer/DIContainer.csproj

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<OutputType>Exe</OutputType>
5+
<TargetFramework>net7.0</TargetFramework>
6+
<ImplicitUsings>enable</ImplicitUsings>
7+
<Nullable>enable</Nullable>
8+
</PropertyGroup>
9+
10+
</Project>

Diff for: DIContainer/DIContainer.sln

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
2+
Microsoft Visual Studio Solution File, Format Version 12.00
3+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DIContainer", "DIContainer.csproj", "{A320B5F7-DBB3-449C-9DF3-997B28E1C109}"
4+
EndProject
5+
Global
6+
GlobalSection(SolutionConfigurationPlatforms) = preSolution
7+
Debug|Any CPU = Debug|Any CPU
8+
Release|Any CPU = Release|Any CPU
9+
EndGlobalSection
10+
GlobalSection(ProjectConfigurationPlatforms) = postSolution
11+
{A320B5F7-DBB3-449C-9DF3-997B28E1C109}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
12+
{A320B5F7-DBB3-449C-9DF3-997B28E1C109}.Debug|Any CPU.Build.0 = Debug|Any CPU
13+
{A320B5F7-DBB3-449C-9DF3-997B28E1C109}.Release|Any CPU.ActiveCfg = Release|Any CPU
14+
{A320B5F7-DBB3-449C-9DF3-997B28E1C109}.Release|Any CPU.Build.0 = Release|Any CPU
15+
EndGlobalSection
16+
EndGlobal

Diff for: DIContainer/IMultiplier.cs

+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
namespace DIContainer;
2+
3+
public interface IMultiplier
4+
{
5+
int Multiply(int a, int b);
6+
}
7+
8+
public class Multiplier : IMultiplier
9+
{
10+
private readonly ICalculator _calculator;
11+
12+
public Multiplier(ICalculator calculator)
13+
{
14+
_calculator = calculator;
15+
}
16+
17+
public int Multiply(int a, int b)
18+
{
19+
if (a == 0 || b == 0)
20+
return 0;
21+
22+
var result = 0;
23+
for (var i = 0; i < Math.Abs(b); i++)
24+
result += a;
25+
26+
return Math.Sign(b) == 1 ? result : -result;
27+
}
28+
}

Diff for: DIContainer/Program.cs

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// See https://aka.ms/new-console-template for more information
2+
3+
using System.Threading.Channels;
4+
using DIContainer;
5+
6+
var container = new Container();
7+
container.RegisterSingleton<ICalculator, Calculator>();
8+
container.Register<IMultiplier, Multiplier>();
9+
10+
var calculator = container.Resolve<IMultiplier>();
11+
12+
Console.WriteLine(calculator.Multiply(2, 3));
13+
14+
Console.WriteLine($"First call to GetHashCode in ICalculator: {container.Resolve<ICalculator>().GetHashCode()}");
15+
Console.WriteLine($"Second call to GetHashCode in ICalculator: {container.Resolve<ICalculator>().GetHashCode()}");
16+
17+
Console.WriteLine($"First call to GetHashCode in IMultiplier: {container.Resolve<IMultiplier>().GetHashCode()}");
18+
Console.WriteLine($"Second call to GetHashCode in IMultiplier: {container.Resolve<IMultiplier>().GetHashCode()}");

Diff for: DIContainer/README.md

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# Simple DI - Container
2+
3+
This blog post will show you a very simple **Dependency Injection** container.
4+
5+
This will give a better understanding of what **Dependency Injection** is and how it is done. And sure we will see how this is related to **IoC** - Inversion of Control.
6+
7+
Found [here](https://steven-giesel.com/blogPost/8cd29874-1bb5-4df5-8e0a-293a293eb25a)

Diff for: README.md

+1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ Contains all of my examples from various blog posts. You can find a comprehensiv
44

55
| BlogPost | Publish Date |
66
| ---------------------------------------------------------------------------------------- | ------------ |
7+
| [Simple DI - Container](DIContainer/) | 13.12.2022 |
78
| [How to enumerate through a StringBuilder](EnumerateStringBuilder/) | 03.12.2022 |
89
| [Frozen collections in .NET 8](FrozenSetBenchmark/) | 24.11.2022 |
910
| ["Use always a StringBuilder" - Internet myths](StringBuilderPerformance/) | 20.11.2022 |

0 commit comments

Comments
 (0)