Skip to content

Commit d3bbf74

Browse files
committed
Some small readability improvements and performance improvements in GenericRegistrationEntry.
1 parent 65d30d0 commit d3bbf74

File tree

3 files changed

+87
-57
lines changed

3 files changed

+87
-57
lines changed

src/SimpleInjector/Container.Common.cs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,12 +38,11 @@ namespace SimpleInjector
3838
[DebuggerTypeProxy(typeof(ContainerDebugView))]
3939
public partial class Container : ApiObject, IDisposable
4040
{
41-
internal readonly Dictionary<object, Dictionary<Type, WeakReference>> LifestyleRegistrationCache =
42-
new Dictionary<object, Dictionary<Type, WeakReference>>();
41+
internal readonly Dictionary<object, Dictionary<Type, WeakReference>> LifestyleRegistrationCache = new();
4342

4443
private static long counter;
4544

46-
private readonly object locker = new object();
45+
private readonly object locker = new();
4746
private readonly List<IInstanceInitializer> instanceInitializers = new();
4847
private readonly List<ContextualResolveInterceptor> resolveInterceptors = new();
4948

src/SimpleInjector/Internals/GenericRegistrationEntry.cs

Lines changed: 84 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,17 @@ namespace SimpleInjector.Internals
1010
internal sealed class GenericRegistrationEntry : IRegistrationEntry
1111
{
1212
private readonly List<IProducerProvider> providers = new();
13-
private readonly Container container;
13+
private readonly ContainerOptions options;
1414

1515
internal GenericRegistrationEntry(Container container)
1616
{
17-
this.container = container;
17+
this.options = container.Options;
1818
}
1919

20+
private Container Container => this.options.Container;
21+
private bool AllowOverridingRegistrations => this.options.AllowOverridingRegistrations;
22+
private bool IsEmpty => this.providers.Count == 0;
23+
2024
private interface IProducerProvider
2125
{
2226
bool IsConditional { get; }
@@ -43,14 +47,18 @@ private interface IProducerProvider
4347

4448
public void Add(InstanceProducer producer)
4549
{
46-
this.container.ThrowWhenContainerIsLockedOrDisposed();
50+
this.Container.ThrowWhenContainerIsLockedOrDisposed();
4751

4852
this.ThrowWhenConditionalIsRegisteredInOverridingMode(producer);
49-
this.ThrowWhenOverlappingRegistrationsExist(producer);
5053

51-
if (this.container.Options.AllowOverridingRegistrations)
54+
if (!this.AllowOverridingRegistrations)
55+
{
56+
this.ThrowWhenOverlappingRegistrationsExist(producer); // O(n) operation
57+
}
58+
59+
if (this.AllowOverridingRegistrations)
5260
{
53-
this.providers.RemoveAll(p => p.ServiceType == producer.ServiceType);
61+
this.providers.RemoveAll(p => p.ServiceType == producer.ServiceType); // O(n) operation
5462
}
5563

5664
this.providers.Add(new ClosedToInstanceProducerProvider(producer));
@@ -62,16 +70,16 @@ public void AddGeneric(
6270
Lifestyle lifestyle,
6371
Predicate<PredicateContext>? predicate)
6472
{
65-
this.container.ThrowWhenContainerIsLockedOrDisposed();
73+
this.Container.ThrowWhenContainerIsLockedOrDisposed();
6674

6775
var provider = new OpenGenericToInstanceProducerProvider(
68-
serviceType, implementationType, lifestyle, predicate, this.container);
76+
serviceType, implementationType, lifestyle, predicate, this.Container);
6977

7078
this.ThrowWhenConditionalIsRegisteredInOverridingMode(provider);
7179

7280
this.ThrowWhenProviderToRegisterOverlapsWithExistingProvider(provider);
7381

74-
if (this.container.Options.AllowOverridingRegistrations)
82+
if (this.AllowOverridingRegistrations)
7583
{
7684
if (provider.GetAppliesToAllClosedServiceTypes())
7785
{
@@ -88,10 +96,10 @@ public void Add(
8896
Lifestyle lifestyle,
8997
Predicate<PredicateContext>? predicate)
9098
{
91-
this.container.ThrowWhenContainerIsLockedOrDisposed();
99+
this.Container.ThrowWhenContainerIsLockedOrDisposed();
92100

93101
var provider = new OpenGenericToInstanceProducerProvider(
94-
serviceType, implementationTypeFactory, lifestyle, predicate, this.container);
102+
serviceType, implementationTypeFactory, lifestyle, predicate, this.Container);
95103

96104
this.ThrowWhenConditionalIsRegisteredInOverridingMode(provider);
97105

@@ -121,9 +129,11 @@ public void Add(
121129
}
122130
}
123131

132+
// This method is only called when we're about to throw an exception.
124133
public int GetNumberOfConditionalRegistrationsFor(Type serviceType) =>
125134
this.GetConditionalProvidersThatMatchType(serviceType).Count();
126135

136+
// This method is only called when we're about to throw an exception.
127137
private IEnumerable<IProducerProvider> GetConditionalProvidersThatMatchType(Type serviceType) =>
128138
from provider in this.providers
129139
where provider.IsConditional
@@ -132,47 +142,50 @@ where provider.MatchesServiceType(serviceType)
132142

133143
private void ThrowWhenOverlappingRegistrationsExist(InstanceProducer producerToRegister)
134144
{
135-
if (!this.container.Options.AllowOverridingRegistrations)
136-
{
137-
var overlappingProviders =
138-
from provider in this.providers
139-
where provider.OverlapsWith(producerToRegister)
140-
select provider;
145+
var overlappingProvider = this.GetFirstOverlappingProvider(producerToRegister);
141146

142-
if (overlappingProviders.Any())
147+
if (overlappingProvider != null)
148+
{
149+
if (overlappingProvider.ServiceType.IsGenericTypeDefinition())
143150
{
144-
var overlappingProvider = overlappingProviders.First();
151+
// An overlapping provider will always have an ImplementationType, because providers
152+
// with a factory will never be overlapping.
153+
Type implementationType = overlappingProvider.ImplementationType!;
154+
155+
throw new InvalidOperationException(
156+
StringResources.RegistrationForClosedServiceTypeOverlapsWithOpenGenericRegistration(
157+
producerToRegister.ServiceType,
158+
implementationType));
159+
}
145160

146-
if (overlappingProvider.ServiceType.IsGenericTypeDefinition())
147-
{
148-
// An overlapping provider will always have an ImplementationType, because providers
149-
// with a factory will never be overlapping.
150-
Type implementationType = overlappingProvider.ImplementationType!;
151-
152-
throw new InvalidOperationException(
153-
StringResources.RegistrationForClosedServiceTypeOverlapsWithOpenGenericRegistration(
154-
producerToRegister.ServiceType,
155-
implementationType));
156-
}
161+
bool eitherOneRegistrationIsConditional =
162+
overlappingProvider.IsConditional != producerToRegister.IsConditional;
157163

158-
bool eitherOneRegistrationIsConditional =
159-
overlappingProvider.IsConditional != producerToRegister.IsConditional;
164+
throw eitherOneRegistrationIsConditional
165+
? GetAnOverlappingGenericRegistrationExistsException(
166+
new ClosedToInstanceProducerProvider(producerToRegister),
167+
overlappingProvider)
168+
: new InvalidOperationException(
169+
StringResources.TypeAlreadyRegistered(producerToRegister.ServiceType));
170+
}
171+
}
160172

161-
throw eitherOneRegistrationIsConditional
162-
? GetAnOverlappingGenericRegistrationExistsException(
163-
new ClosedToInstanceProducerProvider(producerToRegister),
164-
overlappingProvider)
165-
: new InvalidOperationException(
166-
StringResources.TypeAlreadyRegistered(producerToRegister.ServiceType));
173+
private IProducerProvider? GetFirstOverlappingProvider(InstanceProducer producerToRegister)
174+
{
175+
foreach (var provider in this.providers)
176+
{
177+
if (provider.OverlapsWith(producerToRegister))
178+
{
179+
return provider;
167180
}
168181
}
182+
183+
return null;
169184
}
170185

171186
private void ThrowWhenConditionalIsRegisteredInOverridingMode(InstanceProducer producer)
172187
{
173-
if (producer.IsConditional
174-
&& this.container.Options.AllowOverridingRegistrations
175-
&& this.providers.Any())
188+
if (!this.IsEmpty && producer.IsConditional && this.AllowOverridingRegistrations)
176189
{
177190
throw new NotSupportedException(
178191
StringResources.MakingConditionalRegistrationsInOverridingModeIsNotSupported());
@@ -182,8 +195,8 @@ private void ThrowWhenConditionalIsRegisteredInOverridingMode(InstanceProducer p
182195
private void ThrowWhenConditionalIsRegisteredInOverridingMode(
183196
OpenGenericToInstanceProducerProvider provider)
184197
{
185-
if (this.providers.Count > 0
186-
&& this.container.Options.AllowOverridingRegistrations
198+
if (!this.IsEmpty
199+
&& this.AllowOverridingRegistrations
187200
&& !provider.GetAppliesToAllClosedServiceTypes())
188201
{
189202
if (provider.Predicate != null)
@@ -203,37 +216,54 @@ private void ThrowWhenProviderToRegisterOverlapsWithExistingProvider(
203216
OpenGenericToInstanceProducerProvider providerToRegister)
204217
{
205218
bool providerToRegisterIsSuperset =
206-
this.providers.Count > 0 && providerToRegister.GetAppliesToAllClosedServiceTypes();
219+
!this.IsEmpty && providerToRegister.GetAppliesToAllClosedServiceTypes();
207220

208221
// A provider with AppliesToAllClosedServiceTypes true will always have an ImplementationType,
209222
// because the property will always be false for providers with a factory.
210223
Type providerImplementationType = providerToRegister.ImplementationType!;
211224

212-
bool isReplacement = this.container.Options.AllowOverridingRegistrations
225+
bool isReplacement = this.AllowOverridingRegistrations
213226
&& providerToRegister.GetAppliesToAllClosedServiceTypes();
214227

215228
// A provider is a superset of the providerToRegister when it can be applied to ALL generic
216229
// types that the providerToRegister can be applied to as well.
217-
var supersetProviders = this.GetSupersetProvidersFor(providerImplementationType);
230+
var supersetProvider = this.GetFirstOrDefaultSupersetProvidersFor(providerImplementationType);
218231

219-
bool overlaps = providerToRegisterIsSuperset || supersetProviders.Any();
232+
bool overlaps = providerToRegisterIsSuperset || supersetProvider != null;
220233

221234
if (!isReplacement && overlaps)
222235
{
223-
var overlappingProvider = supersetProviders.FirstOrDefault() ?? this.providers[0];
236+
var overlappingProvider = supersetProvider ?? this.providers[0];
224237

225238
throw GetAnOverlappingGenericRegistrationExistsException(
226239
providerToRegister,
227240
overlappingProvider);
228241
}
229242
}
230243

231-
private IEnumerable<IProducerProvider> GetSupersetProvidersFor(Type implementationType) =>
232-
from provider in this.providers
233-
where implementationType != null
234-
where provider.ImplementationType != null
235-
where provider.ImplementationType == implementationType || provider.GetAppliesToAllClosedServiceTypes()
236-
select provider;
244+
private IProducerProvider? GetFirstOrDefaultSupersetProvidersFor(Type implementationType)
245+
{
246+
if (implementationType is null || this.IsEmpty)
247+
{
248+
return null;
249+
}
250+
else
251+
{
252+
foreach (var provider in this.providers)
253+
{
254+
if (provider.ImplementationType != null)
255+
{
256+
if (provider.ImplementationType == implementationType
257+
|| provider.GetAppliesToAllClosedServiceTypes())
258+
{
259+
return provider;
260+
}
261+
}
262+
}
263+
264+
return null;
265+
}
266+
}
237267

238268
private static InvalidOperationException GetAnOverlappingGenericRegistrationExistsException(
239269
IProducerProvider providerToRegister, IProducerProvider overlappingProvider) => new(

src/SimpleInjector/Internals/IRegistrationEntry.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ void Add(
2626

2727
InstanceProducer? TryGetInstanceProducer(Type serviceType, InjectionConsumerInfo consumer);
2828

29+
// This method is only called when we're about to throw an exception.
2930
int GetNumberOfConditionalRegistrationsFor(Type serviceType);
3031
}
3132
}

0 commit comments

Comments
 (0)