通用静态 Class 作为服务定位器
Generic Static Class as Service Locator
我正在将服务定位器模式应用为 described in Game Programming 模式,并且想知道可能的通用实现。以下代码 确实 有效,但我对使用既通用又静态的 class 感到困惑。
以下 C# 代码的想法是向应用程序的其他部分提供 [=25=] 服务,仅公开一个接口而不是完整的实现。使用此方法注册的每个服务在应用程序中只会有一个实例,但我希望能够轻松交换 in/out 所提供接口的不同实现。
我的问题是:当我在整个应用程序中使用以下 class 提供不同的服务时,C# 如何知道我指的是不同类型的不同服务?直觉上,我几乎认为静态变量 _service
会被每个新服务覆盖。
public static class ServiceLocator<T>
{
static T _service;
public static T GetService()
{
return _service;
}
public static void Provide(T service)
{
_service = service;
}
}
下面是一些用法:
// Elsewhere, providing:
_camera = new Camera(GraphicsDevice.Viewport);
ServiceLocator<ICamera>.Provide(_camera);
// Elsewhere, usage:
ICamera camera = ServiceLocator<ICamera>.GetService();
// Elsewhere, providing a different service:
CurrentMap = Map.Create(strategy);
ServiceLocator<IMap>.Provide(CurrentMap);
// Elsewhere, using this different service:
IMap map = ServiceLocator<IMap>.GetService();
C# 为开放类型的泛型参数的每个组合创建一个单独的封闭类型。
由于泛型参数的每个组合都会创建一个单独的 class,因此调用静态构造函数并为每个参数创建自己的成员。
您可以将它们视为不同的 classes。
public static class GenericCounter<T>
{
public static int Count { get; set; } = 0;
}
GenericCounter<int>.Count++;
GenericCounter<int>.Count++;
GenericCounter<string>.Count++;
Console.WriteLine(GenericCounter<double>.Count); // 0
Console.WriteLine(GenericCounter<int>.Count); // 2
Console.WriteLine(GenericCounter<string>.Count); // 1
此代码输出:
0
2
1
例如,在您的情况下,行为将 与您创建两个单独的 classes:
相同
public static class ServiceLocatorOfIMap
{
static IMap _service;
public static IMap GetService()
{
return _service;
}
public static void Provide(IMap service)
{
_service = service;
}
}
public static class ServiceLocatorOfICamera
{
static ICamera _service;
public static ICamera GetService()
{
return _service;
}
public static void Provide(ICamera service)
{
_service = service;
}
}
并像这样使用它:
// Elsewhere, providing:
_camera = new Camera(GraphicsDevice.Viewport);
ServiceLocatorForICamera.Provide(_camera);
// Elsewhere, usage:
ICamera camera = ServiceLocatorForICamera.GetService();
// Elsewhere, providing a different service:
CurrentMap = Map.Create(strategy);
ServiceLocatorForIMap.Provide(CurrentMap);
// Elsewhere, using this different service:
IMap map = ServiceLocatorForIMap.GetService();
总的来说和C#遇到静态泛型classes时的做法类似
我将它用于我不能完全使用依赖注入的情况(比如 WebForms),但我想编写可测试的 classes 并由 DI 容器解析。
用法看起来像
using(var resolved = new ResolvedService<ISomeService>())
{
resolved.Service.DoSomething();
}
好的:
- 可以使用DI容器来解析和释放资源
- 是一次性的,disposing会导致容器释放资源
- 它使容器和服务注册不可见
差:
- 它需要一个静态 class,但它也在合成根中,所以还不错。
- 正如所写,它直接取决于温莎。用任何其他容器替换它很容易。也许有一天我会把它分开,这样
ServiceLocator
就不会耦合到任何特定的容器。但现在改变它是微不足道的。
使用这意味着虽然较大的组件(如 .aspx 页面)不可测试,但我注入其中的内容是可测试的。它只是给了我一个疯狂的想法——我可以为 WebForms 页面编写编排器,这样它们大部分都是可测试的。但希望我永远不需要那样做。
internal class ServiceLocator
{
private static IWindsorContainer _container;
internal static void Initialize(IWindsorContainer container)
{
_container = container;
}
internal static TService Resolve<TService>(string key = null)
{
if (_container == null)
{
throw new InvalidOperationException(
"ServiceLocator must be initialized with a container by calling Initialize(container).");
}
try
{
return string.IsNullOrEmpty(key)
? _container.Resolve<TService>()
: _container.Resolve<TService>(key);
}
catch (ComponentNotFoundException ex)
{
throw new InvalidOperationException(string.Format("No component for {0} has been registered.", typeof(TService).FullName), ex);
}
}
internal static void Release(object resolved)
{
_container.Release(resolved);
}
}
public class ResolvedService<TService> : IDisposable
{
private bool _disposed;
private readonly TService _resolvedInstance;
public TService Service
{
get { return _resolvedInstance; }
}
public ResolvedService(string key = null)
{
_resolvedInstance = ServiceLocator.Resolve<TService>(key);
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
~ResolvedService()
{
Dispose(false);
}
protected virtual void Dispose(bool disposing)
{
if (_disposed) return;
ServiceLocator.Release(_resolvedInstance);
_disposed = true;
}
}
我正在将服务定位器模式应用为 described in Game Programming 模式,并且想知道可能的通用实现。以下代码 确实 有效,但我对使用既通用又静态的 class 感到困惑。
以下 C# 代码的想法是向应用程序的其他部分提供 [=25=] 服务,仅公开一个接口而不是完整的实现。使用此方法注册的每个服务在应用程序中只会有一个实例,但我希望能够轻松交换 in/out 所提供接口的不同实现。
我的问题是:当我在整个应用程序中使用以下 class 提供不同的服务时,C# 如何知道我指的是不同类型的不同服务?直觉上,我几乎认为静态变量 _service
会被每个新服务覆盖。
public static class ServiceLocator<T>
{
static T _service;
public static T GetService()
{
return _service;
}
public static void Provide(T service)
{
_service = service;
}
}
下面是一些用法:
// Elsewhere, providing:
_camera = new Camera(GraphicsDevice.Viewport);
ServiceLocator<ICamera>.Provide(_camera);
// Elsewhere, usage:
ICamera camera = ServiceLocator<ICamera>.GetService();
// Elsewhere, providing a different service:
CurrentMap = Map.Create(strategy);
ServiceLocator<IMap>.Provide(CurrentMap);
// Elsewhere, using this different service:
IMap map = ServiceLocator<IMap>.GetService();
C# 为开放类型的泛型参数的每个组合创建一个单独的封闭类型。
由于泛型参数的每个组合都会创建一个单独的 class,因此调用静态构造函数并为每个参数创建自己的成员。
您可以将它们视为不同的 classes。
public static class GenericCounter<T>
{
public static int Count { get; set; } = 0;
}
GenericCounter<int>.Count++;
GenericCounter<int>.Count++;
GenericCounter<string>.Count++;
Console.WriteLine(GenericCounter<double>.Count); // 0
Console.WriteLine(GenericCounter<int>.Count); // 2
Console.WriteLine(GenericCounter<string>.Count); // 1
此代码输出:
0
2
1
例如,在您的情况下,行为将 与您创建两个单独的 classes:
相同public static class ServiceLocatorOfIMap
{
static IMap _service;
public static IMap GetService()
{
return _service;
}
public static void Provide(IMap service)
{
_service = service;
}
}
public static class ServiceLocatorOfICamera
{
static ICamera _service;
public static ICamera GetService()
{
return _service;
}
public static void Provide(ICamera service)
{
_service = service;
}
}
并像这样使用它:
// Elsewhere, providing:
_camera = new Camera(GraphicsDevice.Viewport);
ServiceLocatorForICamera.Provide(_camera);
// Elsewhere, usage:
ICamera camera = ServiceLocatorForICamera.GetService();
// Elsewhere, providing a different service:
CurrentMap = Map.Create(strategy);
ServiceLocatorForIMap.Provide(CurrentMap);
// Elsewhere, using this different service:
IMap map = ServiceLocatorForIMap.GetService();
总的来说和C#遇到静态泛型classes时的做法类似
我将它用于我不能完全使用依赖注入的情况(比如 WebForms),但我想编写可测试的 classes 并由 DI 容器解析。
用法看起来像
using(var resolved = new ResolvedService<ISomeService>())
{
resolved.Service.DoSomething();
}
好的:
- 可以使用DI容器来解析和释放资源
- 是一次性的,disposing会导致容器释放资源
- 它使容器和服务注册不可见
差:
- 它需要一个静态 class,但它也在合成根中,所以还不错。
- 正如所写,它直接取决于温莎。用任何其他容器替换它很容易。也许有一天我会把它分开,这样
ServiceLocator
就不会耦合到任何特定的容器。但现在改变它是微不足道的。
使用这意味着虽然较大的组件(如 .aspx 页面)不可测试,但我注入其中的内容是可测试的。它只是给了我一个疯狂的想法——我可以为 WebForms 页面编写编排器,这样它们大部分都是可测试的。但希望我永远不需要那样做。
internal class ServiceLocator
{
private static IWindsorContainer _container;
internal static void Initialize(IWindsorContainer container)
{
_container = container;
}
internal static TService Resolve<TService>(string key = null)
{
if (_container == null)
{
throw new InvalidOperationException(
"ServiceLocator must be initialized with a container by calling Initialize(container).");
}
try
{
return string.IsNullOrEmpty(key)
? _container.Resolve<TService>()
: _container.Resolve<TService>(key);
}
catch (ComponentNotFoundException ex)
{
throw new InvalidOperationException(string.Format("No component for {0} has been registered.", typeof(TService).FullName), ex);
}
}
internal static void Release(object resolved)
{
_container.Release(resolved);
}
}
public class ResolvedService<TService> : IDisposable
{
private bool _disposed;
private readonly TService _resolvedInstance;
public TService Service
{
get { return _resolvedInstance; }
}
public ResolvedService(string key = null)
{
_resolvedInstance = ServiceLocator.Resolve<TService>(key);
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
~ResolvedService()
{
Dispose(false);
}
protected virtual void Dispose(bool disposing)
{
if (_disposed) return;
ServiceLocator.Release(_resolvedInstance);
_disposed = true;
}
}