.NET Core中何时使用TryAddSingleton与AddSingleton?
Great question—this is a common point of confusion when working with .NET Core's DI system, and the short answer is: no, TryAddSingleton isn't always "safer"—it depends entirely on your intent. Let's break down the differences and appropriate use cases:
Core Behavior Differences
First, let's recap the key behavior you noticed from decompiling:
AddSingleton<TService, TImplementation>(): Adds the service descriptor every time you call it, even if the sameTServicetype is already registered. This means you can end up with multiple registrations for the same service type (when resolvingIEnumerable<TService>, you'll get all of them; when resolving a single instance, the last registered one wins by default).TryAddSingleton<TService, TImplementation>(): Only adds the descriptor if no registrations exist yet forTService. If there's any existing registration (even a different implementation), it does nothing.
When TryAddSingleton Is the Right Choice
Use this method when you want to:
- Provide a default implementation without overriding existing ones: For example, if you're building a class library and want to register a service, but you don't want to overwrite any custom implementations the host application might have already registered. This respects the "host first" principle.
- Avoid accidental duplicate registrations: If you're unsure whether a service might have been registered elsewhere (like in another library or a startup extension method), and you don't need multiple implementations,
TryAddSingletonprevents redundant entries in the DI container.
When TryAddSingleton Is NOT the Right Choice
Don't use this method if:
- You need to override an existing implementation: If your goal is to replace a default service with your own custom one,
TryAddSingletonwill fail here—if the service is already registered, your implementation won't be added. Instead, useAddSingleton(which appends your registration, making it the default resolved instance) or combineRemoveAll<TService>()withAddSingleton()to fully replace existing registrations. - You need multiple implementations of the same service: For scenarios like decorator patterns, or when you want to resolve all instances of a service type (via
IEnumerable<TService>),TryAddSingletononly adds your implementation once. You'll needAddSingletonto register multiple distinct implementations.
Example Scenario
Suppose your app already has:
services.AddSingleton<IMyService, DefaultMyService>();
- If you call
services.TryAddSingleton<IMyService, CustomMyService>(): TheCustomMyServicewon't be registered—resolvingIMyServicestill givesDefaultMyService. - If you call
services.AddSingleton<IMyService, CustomMyService>(): Resolving a singleIMyServicegivesCustomMyService(last registration wins), and resolvingIEnumerable<IMyService>gives bothDefaultMyServiceandCustomMyService.
Key Takeaway
"Safe" is subjective here. TryAddSingleton is safe for avoiding unintended overrides, but it's unsafe if you need your implementation to be used. Always choose based on whether you want to augment, replace, or provide a fallback for a service registration.
内容的提问来源于stack exchange,提问作者Michael Freidgeim

