IDisposable 的实际实现
Actual implementation of IDisposable
注意:标题是一个问题,但我实际上有3个问题要问。
我正在使用 Autofac DI 容器来解决依赖关系。
如果此代码还有其他可以改进的地方,请提出建议。
我已经浏览了以下链接:
Implementing IDisposable correctly
Implementing a Dispose method
IDisposable Part 1
考虑以下代码:
接口:IHttpClient
public interface IHttpClient : IDisposable
{
HttpRequestHeaders DefaultRequestHeaders { get; set; } // Question 1
Uri BaseAddress { get; set; }
Task<HttpResponseMessage> PostAsync(string uri, HttpContent httpContent);
}
Class: HttpClientAdaptor
internal HttpClientAdaptor : IHttpClient
{
private HttpRequestHeaders _defaultRequestHeaders;
private bool _disposed = false;
public HttpRequestHeaders DefaultRequestHeaders
{
get
{
if (_defaultRequestHeaders == null)
_defaultRequestHeaders = new HttpClient().DefaultRequestHeaders;
return _defaultRequestHeaders;
}
set
{
if (value != null)
_defaultRequestHeaders = value;
}
}
public Uri BaseAddress { get; set; }
private async Task<HttpResponseMessage> PostAsync(Uri uri, HttpContent httpContent, CancellationToken cancellationToken)
{
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, uri)
{
Content = httpContent
};
return await new HttpMessageInvoker(new HttpClientHandler()).SendAsync(request, cancellationToken); // Question 2
}
protected virtual void Dispose(bool disposing)
{
if (!_disposed)
{
if (disposing)
{
_defaultRequestHeaders = null;
BaseAddress = new Uri(); // Question 3
}
_disposed = true;
}
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
}
考虑以下 class、接口 IHttpClient
和 // Question 1
:
class DS
{
private IHttpClient _apiClient;
public class DS(/*other dependencies*/, IHttpClient apiClient)
{
_apiClient = apiClient;
}
// At somepoint I need to perform the following operation
private void SomeMethod(string hostname, /*other params*/)
{
_apiClient.BaseAddress = new Uri(hostName);
_apiClient.DefaultRequestHeaders.Accept.Clear(); // this line throws exception as "DefaultRequestHeaders" is null
_apiClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
}
}
问题 1:当我已经解决了依赖关系时,为什么会抛出空异常。为避免这种情况,我必须添加以下行:
_apiClient.DefaultRequestHeaders = new HttpClientAdaptor().DefaultRequestHeaders;
有没有更好的方法来做到这一点,而不是在我需要的任何地方使用上面提到的代码DefaultRequestHeader
?
问题 2:考虑 class HttpClientAdaptor
和评论 // Question 2
。我需要打电话给 SendAsync()
。这是正确的方法还是我应该继承 class HttpMessageInvoker
。或者有第三种方法吗?
问题 3:考虑 class HttpClientAdaptor
和评论 // Question 3
。这是处理托管代码的正确方法吗?如果是那么我应该如何处理 属性 BaseAddress
?如果没有,请提出正确的方法。当然,由于显而易见的原因,当前代码会给出编译时错误。
问题一:
如评论中所述,没有 DI 通过调用 get 属性来初始化您的字段,因此您需要做的是将初始化移动到构造函数中。
问题 2:有点不清楚你想要实现什么,正如你在评论中指出的那样,你将它用于单元测试。一个人不在模拟中进行实际调用。那么也许您还需要其他东西?例如。记住调用发生了,可能还有调用时使用的参数。
问题三:
不,它似乎不是处理您实施它的方式的正确方法。在这两种情况下需要实施 IDisposable
:
- 您有一个
IDisposable
的会员,您需要在 上致电 Dispose
- 您直接有一些非托管资源
这些似乎都不是你的情况,所以你真的不需要在 dispose 中做任何事情,因为看起来只有你拥有它,因为你正在重新 -实现系统接口。
这仍然意味着那些将使用您的 HttpClientAdaptor
class 的人需要进行处理,因为他们不知道它实际上什么都不做。当然,如果您自己使用它,您可以跳过调用 dispose 而不会产生任何后果。
P.S。如果您使用 class 进行单元测试,则可以使用专用于此目的的模拟库之一,并将动态完成这项工作。 Moq
或 NSubstitute
是不错的候选人。
注意:标题是一个问题,但我实际上有3个问题要问。
我正在使用 Autofac DI 容器来解决依赖关系。
如果此代码还有其他可以改进的地方,请提出建议。
我已经浏览了以下链接:
Implementing IDisposable correctly
Implementing a Dispose method
IDisposable Part 1
考虑以下代码:
接口:IHttpClient
public interface IHttpClient : IDisposable
{
HttpRequestHeaders DefaultRequestHeaders { get; set; } // Question 1
Uri BaseAddress { get; set; }
Task<HttpResponseMessage> PostAsync(string uri, HttpContent httpContent);
}
Class: HttpClientAdaptor
internal HttpClientAdaptor : IHttpClient
{
private HttpRequestHeaders _defaultRequestHeaders;
private bool _disposed = false;
public HttpRequestHeaders DefaultRequestHeaders
{
get
{
if (_defaultRequestHeaders == null)
_defaultRequestHeaders = new HttpClient().DefaultRequestHeaders;
return _defaultRequestHeaders;
}
set
{
if (value != null)
_defaultRequestHeaders = value;
}
}
public Uri BaseAddress { get; set; }
private async Task<HttpResponseMessage> PostAsync(Uri uri, HttpContent httpContent, CancellationToken cancellationToken)
{
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, uri)
{
Content = httpContent
};
return await new HttpMessageInvoker(new HttpClientHandler()).SendAsync(request, cancellationToken); // Question 2
}
protected virtual void Dispose(bool disposing)
{
if (!_disposed)
{
if (disposing)
{
_defaultRequestHeaders = null;
BaseAddress = new Uri(); // Question 3
}
_disposed = true;
}
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
}
考虑以下 class、接口 IHttpClient
和 // Question 1
:
class DS
{
private IHttpClient _apiClient;
public class DS(/*other dependencies*/, IHttpClient apiClient)
{
_apiClient = apiClient;
}
// At somepoint I need to perform the following operation
private void SomeMethod(string hostname, /*other params*/)
{
_apiClient.BaseAddress = new Uri(hostName);
_apiClient.DefaultRequestHeaders.Accept.Clear(); // this line throws exception as "DefaultRequestHeaders" is null
_apiClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
}
}
问题 1:当我已经解决了依赖关系时,为什么会抛出空异常。为避免这种情况,我必须添加以下行:
_apiClient.DefaultRequestHeaders = new HttpClientAdaptor().DefaultRequestHeaders;
有没有更好的方法来做到这一点,而不是在我需要的任何地方使用上面提到的代码DefaultRequestHeader
?
问题 2:考虑 class HttpClientAdaptor
和评论 // Question 2
。我需要打电话给 SendAsync()
。这是正确的方法还是我应该继承 class HttpMessageInvoker
。或者有第三种方法吗?
问题 3:考虑 class HttpClientAdaptor
和评论 // Question 3
。这是处理托管代码的正确方法吗?如果是那么我应该如何处理 属性 BaseAddress
?如果没有,请提出正确的方法。当然,由于显而易见的原因,当前代码会给出编译时错误。
问题一: 如评论中所述,没有 DI 通过调用 get 属性来初始化您的字段,因此您需要做的是将初始化移动到构造函数中。
问题 2:有点不清楚你想要实现什么,正如你在评论中指出的那样,你将它用于单元测试。一个人不在模拟中进行实际调用。那么也许您还需要其他东西?例如。记住调用发生了,可能还有调用时使用的参数。
问题三:
不,它似乎不是处理您实施它的方式的正确方法。在这两种情况下需要实施 IDisposable
:
- 您有一个
IDisposable
的会员,您需要在 上致电 - 您直接有一些非托管资源
Dispose
这些似乎都不是你的情况,所以你真的不需要在 dispose 中做任何事情,因为看起来只有你拥有它,因为你正在重新 -实现系统接口。
这仍然意味着那些将使用您的 HttpClientAdaptor
class 的人需要进行处理,因为他们不知道它实际上什么都不做。当然,如果您自己使用它,您可以跳过调用 dispose 而不会产生任何后果。
P.S。如果您使用 class 进行单元测试,则可以使用专用于此目的的模拟库之一,并将动态完成这项工作。 Moq
或 NSubstitute
是不错的候选人。