Skip to content

Commit f050f1b

Browse files
committed
2 parents 563a2dc + 65d30d0 commit f050f1b

File tree

1 file changed

+47
-37
lines changed

1 file changed

+47
-37
lines changed

src/SimpleInjector/Internals/GenericRegistrationEntry.cs

Lines changed: 47 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ namespace SimpleInjector.Internals
99

1010
internal sealed class GenericRegistrationEntry : IRegistrationEntry
1111
{
12-
private readonly List<IProducerProvider> providers = new List<IProducerProvider>();
12+
private readonly List<IProducerProvider> providers = new();
1313
private readonly Container container;
1414

1515
internal GenericRegistrationEntry(Container container)
@@ -21,7 +21,8 @@ private interface IProducerProvider
2121
{
2222
bool IsConditional { get; }
2323

24-
bool AppliesToAllClosedServiceTypes { get; }
24+
// The first call to this method can be costly to perform.
25+
bool GetAppliesToAllClosedServiceTypes();
2526

2627
Type ServiceType { get; }
2728

@@ -70,9 +71,12 @@ public void AddGeneric(
7071

7172
this.ThrowWhenProviderToRegisterOverlapsWithExistingProvider(provider);
7273

73-
if (provider.AppliesToAllClosedServiceTypes && this.container.Options.AllowOverridingRegistrations)
74+
if (this.container.Options.AllowOverridingRegistrations)
7475
{
75-
this.providers.RemoveAll(p => p.AppliesToAllClosedServiceTypes);
76+
if (provider.GetAppliesToAllClosedServiceTypes())
77+
{
78+
this.providers.RemoveAll(p => p.GetAppliesToAllClosedServiceTypes());
79+
}
7680
}
7781

7882
this.providers.Add(provider);
@@ -178,21 +182,19 @@ private void ThrowWhenConditionalIsRegisteredInOverridingMode(InstanceProducer p
178182
private void ThrowWhenConditionalIsRegisteredInOverridingMode(
179183
OpenGenericToInstanceProducerProvider provider)
180184
{
181-
if (!provider.AppliesToAllClosedServiceTypes
182-
&& this.container.Options.AllowOverridingRegistrations)
185+
if (this.providers.Count > 0
186+
&& this.container.Options.AllowOverridingRegistrations
187+
&& !provider.GetAppliesToAllClosedServiceTypes())
183188
{
184-
if (this.providers.Count > 0)
189+
if (provider.Predicate != null)
185190
{
186-
if (provider.Predicate != null)
187-
{
188-
throw new NotSupportedException(
189-
StringResources.MakingConditionalRegistrationsInOverridingModeIsNotSupported());
190-
}
191-
else
192-
{
193-
throw new NotSupportedException(
194-
StringResources.MakingRegistrationsWithTypeConstraintsInOverridingModeIsNotSupported());
195-
}
191+
throw new NotSupportedException(
192+
StringResources.MakingConditionalRegistrationsInOverridingModeIsNotSupported());
193+
}
194+
else
195+
{
196+
throw new NotSupportedException(
197+
StringResources.MakingRegistrationsWithTypeConstraintsInOverridingModeIsNotSupported());
196198
}
197199
}
198200
}
@@ -201,14 +203,14 @@ private void ThrowWhenProviderToRegisterOverlapsWithExistingProvider(
201203
OpenGenericToInstanceProducerProvider providerToRegister)
202204
{
203205
bool providerToRegisterIsSuperset =
204-
providerToRegister.AppliesToAllClosedServiceTypes && this.providers.Count > 0;
206+
this.providers.Count > 0 && providerToRegister.GetAppliesToAllClosedServiceTypes();
205207

206208
// A provider with AppliesToAllClosedServiceTypes true will always have an ImplementationType,
207209
// because the property will always be false for providers with a factory.
208210
Type providerImplementationType = providerToRegister.ImplementationType!;
209211

210-
bool isReplacement = providerToRegister.AppliesToAllClosedServiceTypes
211-
&& this.container.Options.AllowOverridingRegistrations;
212+
bool isReplacement = this.container.Options.AllowOverridingRegistrations
213+
&& providerToRegister.GetAppliesToAllClosedServiceTypes();
212214

213215
// A provider is a superset of the providerToRegister when it can be applied to ALL generic
214216
// types that the providerToRegister can be applied to as well.
@@ -230,13 +232,11 @@ private IEnumerable<IProducerProvider> GetSupersetProvidersFor(Type implementati
230232
from provider in this.providers
231233
where implementationType != null
232234
where provider.ImplementationType != null
233-
where provider.AppliesToAllClosedServiceTypes
234-
|| provider.ImplementationType == implementationType
235+
where provider.ImplementationType == implementationType || provider.GetAppliesToAllClosedServiceTypes()
235236
select provider;
236237

237238
private static InvalidOperationException GetAnOverlappingGenericRegistrationExistsException(
238-
IProducerProvider providerToRegister, IProducerProvider overlappingProvider) =>
239-
new InvalidOperationException(
239+
IProducerProvider providerToRegister, IProducerProvider overlappingProvider) => new(
240240
StringResources.AnOverlappingRegistrationExists(
241241
providerToRegister.ServiceType,
242242
// ImplementationType will never be null, because providers can never be overlapping when they
@@ -287,7 +287,7 @@ public ClosedToInstanceProducerProvider(InstanceProducer producer)
287287
}
288288

289289
public bool IsConditional => this.producer.IsConditional;
290-
public bool AppliesToAllClosedServiceTypes => false;
290+
public bool GetAppliesToAllClosedServiceTypes() => false;
291291
public Type ServiceType => this.producer.ServiceType;
292292
public Type? ImplementationType => this.producer.Registration.ImplementationType;
293293
public IEnumerable<InstanceProducer> CurrentProducers => Enumerable.Repeat(this.producer, 1);
@@ -311,14 +311,14 @@ private sealed class OpenGenericToInstanceProducerProvider : IProducerProvider
311311
{
312312
internal readonly Predicate<PredicateContext>? Predicate;
313313

314-
private readonly Dictionary<object, InstanceProducer> cache =
315-
new Dictionary<object, InstanceProducer>();
316-
private readonly Dictionary<Type, Registration> registrationCache =
317-
new Dictionary<Type, Registration>();
314+
private readonly Dictionary<object, InstanceProducer> cache = new();
315+
private readonly Dictionary<Type, Registration> registrationCache = new();
318316

319317
private readonly Lifestyle lifestyle;
320318
private readonly Container container;
321319

320+
private bool? appliesToAllClosedServiceTypes;
321+
322322
internal OpenGenericToInstanceProducerProvider(
323323
Type serviceType,
324324
Type implementationType,
@@ -332,11 +332,6 @@ internal OpenGenericToInstanceProducerProvider(
332332
this.lifestyle = lifestyle;
333333
this.Predicate = predicate;
334334
this.container = container;
335-
336-
// We cache the result of this method, because this is a really heavy operation.
337-
// Not caching it can dramatically influence the performance of the registration process.
338-
this.AppliesToAllClosedServiceTypes =
339-
this.RegistrationAppliesToAllClosedServiceTypes(implementationType);
340335
}
341336

342337
internal OpenGenericToInstanceProducerProvider(
@@ -351,11 +346,26 @@ internal OpenGenericToInstanceProducerProvider(
351346
this.lifestyle = lifestyle;
352347
this.Predicate = predicate;
353348
this.container = container;
354-
this.AppliesToAllClosedServiceTypes = false;
349+
this.appliesToAllClosedServiceTypes = false;
355350
}
356351

357352
public bool IsConditional => this.Predicate != null;
358-
public bool AppliesToAllClosedServiceTypes { get; }
353+
354+
// I turned this former property into a method call to make it more obvious that this is can be
355+
// a very costly operation (which can also throw first-chance exceptions).
356+
public bool GetAppliesToAllClosedServiceTypes()
357+
{
358+
if (this.appliesToAllClosedServiceTypes is null)
359+
{
360+
// We cache the result of this method. Not caching it can dramatically influence the
361+
// performance of the registration process.
362+
this.appliesToAllClosedServiceTypes =
363+
this.RegistrationAppliesToAllClosedServiceTypes(this.ImplementationType!);
364+
}
365+
366+
return this.appliesToAllClosedServiceTypes.Value;
367+
}
368+
359369
public Type ServiceType { get; }
360370
public Type? ImplementationType { get; }
361371
public Func<TypeFactoryContext, Type> ImplementationTypeFactory { get; }
@@ -462,7 +472,7 @@ private InstanceProducer GetProducer(PredicateContext context)
462472
}
463473

464474
private InstanceProducer CreateNewProducerFor(PredicateContext context) =>
465-
new InstanceProducer(context.ServiceType, this.GetRegistration(context), this.Predicate);
475+
new(context.ServiceType, this.GetRegistration(context), this.Predicate);
466476

467477
private Registration GetRegistration(PredicateContext context)
468478
{

0 commit comments

Comments
 (0)