簡體   English   中英

通用靜態類作為服務定位器

[英]Generic Static Class as Service Locator

我正在應用游戲編程模式中描述的服務定位器模式,並且想知道可能的通用實現。 以下代碼確實有效,但我對使用泛型和靜態類感到困惑。

以下C#代碼的想法是為應用程序的其他部分提供“全局”服務,僅公開接口而不是完整實現。 使用此方法注冊的每個服務在應用程序中只有一個實例,但我希望能夠輕松地交換/提供所提供接口的不同實現。

我的問題是:當我使用以下類在我的應用程序中提供不同的服務時,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#為open類型的每個通用參數組合創建一個單獨的閉合類型。
由於泛型參數的每個組合都會創建一個單獨的類,調用靜態構造函數並為每個類創建自己的成員。 你可以把它們想象成不同的類。

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

例如,在您的情況下,行為將您創建兩個單獨的類相同:

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#在遇到靜態泛型類時所做的事情。

我使用它來處理我不能一直使用依賴注入的情況(比如WebForms),但是我想寫一個由DI容器解析的可測試類。

用法看起來像

using(var resolved = new ResolvedService<ISomeService>())
{
    resolved.Service.DoSomething();
}

好處:

  • 您可以使用DI容器來解析和釋放資源
  • 它是一次性的,處理導致容器釋放資源
  • 它使容器和服務注冊不在視線范圍內

壞事:

  • 它需要一個靜態類,但它也在組合根中,所以它不是太糟糕。
  • 如上所述,它直接取決於溫莎。 用任何其他容器替換它很容易。 也許有一天我會把它分開,以便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;
    }
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM