关于C#:在静态方法中使用singleton实例

Using Singleton Instance inside Static method

我在Web应用程序中有一个静态方法调用。此方法使用其他类的单一实例。这个singleton实例基本上是一个用于发出一些API请求的httpclient。

目前,我没有任何机制来自动对其进行多用户测试。这种方法有什么后果吗?我知道,如果我们不在静态方法中使用静态变量,那么通常的静态方法是线程安全的。但我不知道在单身的情况下它是如何表现的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public static class API
{
    private static System.Net.Http.HttpClient httpClient;

    public static System.Net.Http.HttpClient Instance
    {
        get
        {
            return httpClient ?? (httpClient = new System.Net.Http.HttpClient());
        }
    }
}

public static async Task<string> GetData(string id)
{
    HttpResponseMessage response = await
    API.Instance.GetAsync(string.Format(requestURL, id));
    response.EnsureSuccessStatusCode();

    // return URI of the created resource.
    return await response.Content.ReadAsStringAsync();
}


为了避免线程问题,您至少需要以线程安全的方式实现单例。JonSkeet的这篇文章描述了如何以线程安全的方式创建单例实例。另外,您应该确保singleton的方法既可以处理并行请求,也可以使用lock来同步调用。

静态方法中使用单例的问题也是面向对象设计的问题之一。拥有一个单例并在应用程序的许多地方使用它是方便的,但也有其缺点:

  • 其他开发人员看不到方法依赖于单例实例的第一个外观。这是一种隐藏的依赖关系。
  • 此外,在需要不同行为的情况下(例如在测试中),您不能轻易地更改实例。

因此,我建议考虑单例是否真的是必要的,或者您是否可以将它作为参数注入到需要它的方法中:

1
2
3
4
5
6
7
8
9
public static async Task<string> GetData(API api, string id)
{
    HttpResponseMessage response = await
    api.Instance.GetAsync(string.Format(requestURL, id));
    response.EnsureSuccessStatusCode();

    // return URI of the created resource.
    return await response.Content.ReadAsStringAsync();
}

您可以将方法更改为可调用的扩展方法,如下所示:

1
var result = await API.Instance.GetData("123");

您只需在签名中添加一个this

1
2
3
4
public static async Task<string> GetData(this API api, string id)
{
  // ...
}