1
+ using System . Reflection ;
2
+
1
3
namespace CSharpLibraries . DependencyInjector
2
4
{
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
+ }
3
17
public class DependencyInjector
4
18
{
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
11
20
{
12
- _typesMap [ typeof ( T ) ] = typeof ( T ) ;
21
+ Shared ,
22
+ Unique ,
13
23
}
14
-
15
- public void AddSingeleton < I , T > ( )
16
- where T : I
17
- where I : class
24
+ private class ServiceInfo
18
25
{
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 ;
20
30
}
21
-
22
- public T ? get < T > ( ) where T : class
31
+ private class ServiceKey
23
32
{
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 ;
25
39
}
40
+ private HashSet < Type > underConstruction = new ( ) ;
41
+ private Dictionary < Type , ServiceInfo > _typesMap = new ( ) ;
42
+ private Dictionary < ServiceKey , object > _services = new ( ) ;
26
43
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
28
49
{
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 )
30
66
{
31
- registerObject ( interfaceType ) ;
67
+ return createService ( interfaceType ) ;
32
68
}
69
+ return null ;
33
70
}
34
-
35
- private object registerObject ( Type interfaceType )
71
+ private object getSharedService ( Type interfaceType , string ? token = null )
36
72
{
37
- if ( underConstruction . Contains ( interfaceType ) )
73
+ var key = new ServiceKey { type = interfaceType , token = token } ;
74
+ if ( _services . TryGetValue ( key , out var obj ) )
38
75
{
39
- throw new InvalidOperationException ( $ "Circular dependency detected in { interfaceType . FullName } constructor" ) ;
76
+ return obj ;
40
77
}
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 ;
45
85
return obj ;
46
86
}
47
-
48
- private object createObject ( Type interfaceType )
87
+ private object createService ( Type interfaceType )
49
88
{
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 ) )
54
90
{
55
- throw new InvalidOperationException ( $ "Class { type . FullName } should have exactly one constructor") ;
91
+ throw new InvalidOperationException ( $ "Circular dependency detected in { interfaceType . FullName } constructor") ;
56
92
}
57
- var constructor = constructors . First ( ) ;
58
- var parametersInfo = constructor . GetParameters ( ) ;
93
+ underConstruction . Add ( interfaceType ) ;
59
94
60
- foreach ( var parameterInfo in parametersInfo )
95
+ var constructor = getServiceConstructor ( interfaceType ) ;
96
+ List < object > parameters = new ( ) ;
97
+ foreach ( var parameterInfo in constructor . GetParameters ( ) )
61
98
{
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 ) ;
64
105
}
106
+ underConstruction . Remove ( interfaceType ) ;
65
107
return constructor . Invoke ( parameters . ToArray ( ) ) ;
66
108
}
67
-
68
- private object getParameter ( Type parameterType )
109
+ private ConstructorInfo getServiceConstructor ( Type interfaceType )
69
110
{
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 )
76
114
{
77
- return registerObject ( parameterType ) ;
115
+ throw new InvalidOperationException ( $ "Class { info . type . FullName } should have exactly one constructor" ) ;
78
116
}
117
+ return constructors . First ( ) ;
79
118
}
80
-
81
- private object ? get ( Type interfaceType )
119
+ private object ? getParameterService ( ParameterInfo parameterInfo )
82
120
{
83
- if ( _registeredObjects . TryGetValue ( interfaceType , out var obj ) )
121
+ string ? token = null ;
122
+ ServiceScope ? serviceScope = null ;
123
+ if ( parameterInfo . GetCustomAttributes ( typeof ( UniqueAttribute ) , false ) . Any ( ) )
84
124
{
85
- return obj ;
125
+ serviceScope = ServiceScope . Unique ;
86
126
}
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 ) ;
88
135
}
89
136
}
90
137
}
0 commit comments