我在一些.net核心示例中注意到,在注册服务时会调用TryAddSingleton
,有些AddSingleton
会调用。
Decompiler显示TryAdd(由TryAddSingleton调用) 添加指定的参数"描述符"收集"收集"如果服务类型尚未注册。
这是否意味着使用TryAddSingleton总是更安全,以防某些其他方法/库已经注册了同一个类?
答案 0 :(得分:23)
正如您已经注意到的,TryAddSingleton
和AddSingleton
之间的区别在于AddSingleton
始终将注册附加到集合中,而TryAddSingleton
仅在没有注册时执行此操作注册给定的服务类型。
如果同一服务类型存在多个注册,但请求了一个实例,则.NET Core将始终返回最后一个。这意味着AddSingleton
的行为是替换非集合解析的实例。例如:
services.AddSingleton<IX, A>();
services.AddSingleton<IX, B>(); // ‘replaces’ A
IX x = container.GetService<IX>(); // resolves B
然而,对于集合结果,AddSingleton
表现完全不同,因为在这种情况下AddSingleton
表现为该服务类型的现有注册的集合“附加”。例如:
services.AddSingleton<IX, A>();
services.AddSingleton<IX, B>();
IEnumerable<IX> xs = container.GetServices<IX>(); // resolves A and B
但是,对于TryAddSingleton
,当已存在给定服务类型的注册时,将不会添加注册。这意味着,无论何时将服务类型解析为一个实例或实例集合,在至少有一个注册时都不会添加注册。例如:
services.TryAddSingleton<IX, A>(); // adds A
services.TryAddSingleton<IX, B>(); // does not add B, because of A
IX x = container.GetService<IX>(); // resolves A
services.TryAddSingleton <IX, A>(); // adds A
services.TryAddSingleton <IX, B>(); // does not add B, because of A
IEnumerable<IX> xs = container.GetServices<IX>(); // resolves A
TryAddSingleton
对于希望将自己的组件注册到容器的框架和第三方库代码特别有用。它允许应用程序开发人员覆盖框架或库的默认注册,即使应用程序开发人员在调用框架或第三方AddXXX
扩展方法之前注册了该组件。例如:
services.TryAddSingleton<IX, A>(); // adds A
services.AddThirdPartyLibrary (); // calls services.TryAddSingleton <IX, B>();
IX x = container.GetService<IX>(); // resolves A
第三方库是否已调用AddSingleton
而不是TryAddSingleton
,应用程序开发人员的A
将始终被覆盖,这可能会让开发人员感到困惑。
作为应用程序开发人员,您通常知道自己注册的内容,这使得使用TryAddSingleton
对应用程序开发人员无用。
我甚至认为,从应用程序开发人员的角度来看,AddSingleton
的行为可能非常棘手,因为它隐含地覆盖了现有的注册,没有任何警告。我的经验是,这种行为可能导致很难发现配置错误。更安全的设计应该是AddSingleton
,AppendSingleton
和ReplaceSingleton
方法,其中AddSingleton
会在注册存在的情况下抛出异常,ReplaceSingleton
会实际上扔掉了现有的注册。