Skip to content

Commit 4018a6c

Browse files
committed
expand DI
1 parent 76c1bb7 commit 4018a6c

File tree

3 files changed

+115
-63
lines changed

3 files changed

+115
-63
lines changed
Lines changed: 99 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -1,90 +1,137 @@
1+
using System.Reflection;
2+
13
namespace CSharpLibraries.DependencyInjector
24
{
5+
[System.AttributeUsage(System.AttributeTargets.Parameter)]
6+
public class UniqueAttribute : System.Attribute { }
7+
8+
[System.AttributeUsage(System.AttributeTargets.Parameter)]
9+
public class SharedAttribute : System.Attribute
10+
{
11+
public string token { get; set; }
12+
public SharedAttribute(string token)
13+
{
14+
this.token = token;
15+
}
16+
}
317
public class DependencyInjector
418
{
5-
private HashSet<Type> underConstruction = new();
6-
private Dictionary<Type, Type> _typesMap = new();
7-
private Dictionary<Type, object> _registeredObjects = new();
8-
9-
public void AddSingeleton<T>()
10-
where T : class
19+
private enum ServiceScope
1120
{
12-
_typesMap[typeof(T)] = typeof(T);
21+
Shared,
22+
Unique,
1323
}
14-
15-
public void AddSingeleton<I, T>()
16-
where T : I
17-
where I : class
24+
private class ServiceInfo
1825
{
19-
_typesMap[typeof(I)] = typeof(T);
26+
public Type? type { get; init; }
27+
public ServiceScope scope { get; init; }
28+
public bool isShared => scope == ServiceScope.Shared;
29+
public bool isUnique => scope == ServiceScope.Unique;
2030
}
21-
22-
public T? get<T>() where T : class
31+
private class ServiceKey
2332
{
24-
return get(typeof(T)) as T;
33+
public Type? type { get; set; }
34+
public string? token { get; set; } = null;
35+
36+
public override int GetHashCode() => type.GetHashCode() + token?.GetHashCode() ?? 0;
37+
public override bool Equals(object? obj) => Equals(obj as ServiceKey);
38+
public bool Equals(ServiceKey? obj) => obj != null && obj.type == this.type && token == this.token;
2539
}
40+
private HashSet<Type> underConstruction = new();
41+
private Dictionary<Type, ServiceInfo> _typesMap = new();
42+
private Dictionary<ServiceKey, object> _services = new();
2643

27-
public void build()
44+
public void AddUnique<T>() where T : class => AddUnique<T, T>();
45+
public void AddUnique<I, T>() where T : I where I : class => add<I, T>(ServiceScope.Unique);
46+
public void AddShared<T>() where T : class => AddShared<T, T>();
47+
public void AddShared<I, T>() where T : I where I : class => add<I, T>(ServiceScope.Shared);
48+
private void add<I, T>(ServiceScope scope) where T : I where I : class
2849
{
29-
foreach (var interfaceType in _typesMap.Keys)
50+
_typesMap[typeof(I)] = new ServiceInfo { type = typeof(T), scope = scope };
51+
}
52+
public T? getUnique<T>() where T : class => getService(typeof(T), ServiceScope.Unique) as T;
53+
public T? getShared<T>(string? token = null) where T : class => getService(typeof(T), ServiceScope.Shared, token) as T;
54+
private object? getService(Type interfaceType, ServiceScope? scope = null, string? token = null)
55+
{
56+
if (!_typesMap.TryGetValue(interfaceType, out var info))
57+
{
58+
return null;
59+
}
60+
scope ??= info.scope;
61+
if (info.isShared && scope == ServiceScope.Shared)
62+
{
63+
return getSharedService(interfaceType, token);
64+
}
65+
else if (scope == ServiceScope.Unique)
3066
{
31-
registerObject(interfaceType);
67+
return createService(interfaceType);
3268
}
69+
return null;
3370
}
34-
35-
private object registerObject(Type interfaceType)
71+
private object getSharedService(Type interfaceType, string? token = null)
3672
{
37-
if (underConstruction.Contains(interfaceType))
73+
var key = new ServiceKey { type = interfaceType, token = token };
74+
if (_services.TryGetValue(key, out var obj))
3875
{
39-
throw new InvalidOperationException($"Circular dependency detected in {interfaceType.FullName} constructor");
76+
return obj;
4077
}
41-
underConstruction.Add(interfaceType);
42-
var obj = createObject(interfaceType);
43-
_registeredObjects[interfaceType] = obj;
44-
underConstruction.Remove(interfaceType);
78+
return createAndRegisterService(interfaceType, token);
79+
}
80+
private object createAndRegisterService(Type interfaceType, string? token = null)
81+
{
82+
var key = new ServiceKey { type = interfaceType, token = token };
83+
var obj = createService(interfaceType);
84+
_services[key] = obj;
4585
return obj;
4686
}
47-
48-
private object createObject(Type interfaceType)
87+
private object createService(Type interfaceType)
4988
{
50-
var type = _typesMap[interfaceType];
51-
List<object> parameters = new();
52-
var constructors = type.GetConstructors();
53-
if (constructors.Length != 1)
89+
if (underConstruction.Contains(interfaceType))
5490
{
55-
throw new InvalidOperationException($"Class {type.FullName} should have exactly one constructor");
91+
throw new InvalidOperationException($"Circular dependency detected in {interfaceType.FullName} constructor");
5692
}
57-
var constructor = constructors.First();
58-
var parametersInfo = constructor.GetParameters();
93+
underConstruction.Add(interfaceType);
5994

60-
foreach (var parameterInfo in parametersInfo)
95+
var constructor = getServiceConstructor(interfaceType);
96+
List<object> parameters = new();
97+
foreach (var parameterInfo in constructor.GetParameters())
6198
{
62-
var paramType = parameterInfo.ParameterType;
63-
parameters.Add(getParameter(paramType));
99+
var service = getParameterService(parameterInfo);
100+
if (service is null)
101+
{
102+
throw new InvalidOperationException($"service {parameterInfo.ParameterType.FullName} was not found, check if Scope Attributes match registered service type");
103+
}
104+
parameters.Add(service);
64105
}
106+
underConstruction.Remove(interfaceType);
65107
return constructor.Invoke(parameters.ToArray());
66108
}
67-
68-
private object getParameter(Type parameterType)
109+
private ConstructorInfo getServiceConstructor(Type interfaceType)
69110
{
70-
var obj = get(parameterType);
71-
if (obj is not null)
72-
{
73-
return obj;
74-
}
75-
else
111+
var info = _typesMap[interfaceType];
112+
var constructors = info.type.GetConstructors();
113+
if (constructors.Length != 1)
76114
{
77-
return registerObject(parameterType);
115+
throw new InvalidOperationException($"Class {info.type.FullName} should have exactly one constructor");
78116
}
117+
return constructors.First();
79118
}
80-
81-
private object? get(Type interfaceType)
119+
private object? getParameterService(ParameterInfo parameterInfo)
82120
{
83-
if (_registeredObjects.TryGetValue(interfaceType, out var obj))
121+
string? token = null;
122+
ServiceScope? serviceScope = null;
123+
if (parameterInfo.GetCustomAttributes(typeof(UniqueAttribute), false).Any())
84124
{
85-
return obj;
125+
serviceScope = ServiceScope.Unique;
86126
}
87-
return null;
127+
var sharedAttr = parameterInfo.GetCustomAttributes(typeof(SharedAttribute), false).FirstOrDefault() as SharedAttribute;
128+
if (sharedAttr is not null)
129+
{
130+
serviceScope = ServiceScope.Shared;
131+
token = sharedAttr.token;
132+
}
133+
var paramType = parameterInfo.ParameterType;
134+
return getService(paramType, serviceScope, token);
88135
}
89136
}
90137
}

ExampleClassA.cs

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
using CSharpLibraries.DependencyInjector;
2+
13
namespace CSharpLibraries
24
{
35
public class ExampleClassA
@@ -7,18 +9,13 @@ public ExampleClassA()
79
{
810
id = 101;
911
}
10-
11-
public ExampleClassA(int gg)
12-
{
13-
id = 101;
14-
}
1512
}
1613

1714
public class ExampleClassB
1815
{
1916
public int id = 100;
2017
public ExampleClassA obj;
21-
public ExampleClassB(ExampleClassA exampleClass)
18+
public ExampleClassB([Shared("mytoken")] ExampleClassA exampleClass)
2219
{
2320
id = 101;
2421
obj = exampleClass;

Program.cs

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,18 @@
44

55
var di = new DependencyInjector();
66

7-
di.AddSingeleton<ExampleClassB>();
8-
di.AddSingeleton<ExampleClassA>();
7+
di.AddShared<ExampleClassB>();
8+
di.AddShared<ExampleClassA>();
99

10-
di.build();
10+
var objB = di.getShared<ExampleClassB>();
11+
var objA = objB.obj;
12+
var objAA = di.getShared<ExampleClassA>();
13+
var eq = objA == objAA;
14+
var objAtok = di.getShared<ExampleClassA>("mytoken");
15+
var eqtok = objAtok == objA;
16+
var objun = di.getUnique<ExampleClassA>();
17+
var eqUn = objun == objAA;
18+
var eqTOKen = objAtok == di.getShared<ExampleClassA>("mytoken");
19+
var eqUNI = objun == di.getUnique<ExampleClassA>();
20+
int gg = 1;
1121

12-
var objB = di.get<ExampleClassB>();
13-
var objA = objB.obj;

0 commit comments

Comments
 (0)