英文:
how to not copy code ? generic and non generic implementation - to use same body
问题
请告诉我如何重构这段代码,以便减少复制/粘贴的部分?是否有一种方法可以将通用代码调用为非通用代码?我应该将<object>
传递给通用代码并满足这一点吗?什么是最佳解决方案?
例如,我有以下代码:
public interface IHttpService
{
Task<T> Post<T>(string uri, HttpContent content, int timeOut = 100, CancellationToken token = default);
Task Post(string uri, HttpContent content, int timeOut = 100, CancellationToken token = default);
}
它们几乎相同,但实现不同。
public async Task Post(string uri, HttpContent content, int timeOut = 100, CancellationToken token = default)
{
var request = new HttpRequestMessage(HttpMethod.Post, uri);
request.Content = content;
await sendRequest(request, timeOut, token);
}
public async Task<T> Post<T>(string uri, HttpContent content, int timeOut = 100, CancellationToken token = default)
{
var request = a new HttpRequestMessage(HttpMethod.Post, uri);
request.Content = content;
return await sendRequest<T>(request, timeOut, token);
}
私下定义的sendRequest
方法如下:
private async Task sendRequest(HttpRequestMessage request, int timeOut = 100, CancellationToken token = default)
{
try
{
using var requestCTS = new CancellationTokenSource(TimeSpan.FromSeconds(timeOut));
using var linkedCts = CancellationTokenSource.CreateLinkedTokenSource(requestCTS.Token, token);
using var response = await _httpClient.SendAsync(request, linkedCts.Token);
if (!response.IsSuccessStatusCode)
{
var error = await response.Content.ReadAsStringAsync(token);
throw new Exception(error);
}
}
}
private async Task<T> sendRequest<T>(HttpRequestMessage request, int timeOut = 100, CancellationToken token = default)
{
try
{
using var requestCTS = new CancellationTokenSource(TimeSpan.FromSeconds(timeOut));
using var linkedCts = CancellationTokenSource.CreateLinkedTokenSource(requestCTS.Token, token);
using var response = await _httpClient.SendAsync(request, linkedCts.Token);
if (!response.IsSuccessStatusCode)
{
var error = await response.Content.ReadAsStringAsync(token);
throw new Exception(error);
}
return (await response.Content.ReadFromJsonAsync<T>())!;
}
}
在更新的部分,sendRequest
方法的返回类型已更改:
private async Task<HttpContent> sendRequest(HttpRequestMessage request, int timeOut = 100, CancellationToken token = default)
{
try
{
using var requestCTS = new CancellationTokenSource(TimeSpan.FromSeconds(timeOut));
using var linkedCts = CancellationTokenSource.CreateLinkedTokenSource(requestCTS.Token, token);
using var response = await _httpClient.SendAsync(request, linkedCts.Token);
if (!response.IsSuccessStatusCode)
{
await response.Content.ReadAsStringAsync(token);
throw new Exception(error);
}
return response.Content;
}
catch (AccessTokenNotAvailableException)
{
_navigationManager.NavigateToLogout(StaticStringHelper.LogoutPatch);
return default(HttpContent)!;
}
}
当像下面这样使用它时:
public async Task<T> Get<T>(string uri, int timeOut = 100, CancellationToken token = default)
{
var request = new HttpRequestMessage(HttpMethod.Get, uri);
return await (await sendRequest(request, timeOut, token)).ReadFromJsonAsync<T>(cancellationToken: token);
}
你会遇到异常:
microsoft.AspNetCore.Components.WebAssembly.Rendering.WebAssemblyRenderer[100]
Unhandled exception rendering component: Cannot access a disposed object.
Object name: 'System.Net.Http.BrowserHttpContent'.
System.ObjectDisposedException: Cannot access a disposed object.
Object name: 'System.Net.Http.BrowserHttpContent'.
at System.Net.Http.HttpContent.CheckDisposed()
at System.Net.Http.HttpContent.ReadAsStreamAsync(CancellationToken cancellationToken)
这个异常通常表示HttpContent
对象已被释放。可能需要检查代码中是否有其他地方错误地释放了HttpContent
对象。
英文:
please tell me how should i rework this code so it is not so much copy/paste? is there any way to call generic as nongeneric? should i pass <object>
to generic and be happy with that? what is best solution ?
i have for example
public interface IHttpService
{
Task<T> Post<T>(string uri, HttpContent content, int timeOut = 100, CancellationToken token = default);
Task Post(string uri, HttpContent content, int timeOut = 100, CancellationToken token = default);
}
so they have almost same but diferent implementation
public async Task Post(string uri, HttpContent content, int timeOut = 100, CancellationToken token = default)
{
var request = new HttpRequestMessage(HttpMethod.Post, uri);
request.Content = content;
await sendRequest(request, timeOut, token);
}
public async Task<T> Post<T>(string uri, HttpContent content, int timeOut = 100, CancellationToken token = default)
{
var request = new HttpRequestMessage(HttpMethod.Post, uri);
request.Content = content;
return await sendRequest<T>(request, timeOut, token);
}
private async Task sendRequest(HttpRequestMessage request, int timeOut = 100, CancellationToken token = default)
{
try
{
using var requestCTS = new CancellationTokenSource(TimeSpan.FromSeconds(timeOut));
using var linkedCts = CancellationTokenSource.CreateLinkedTokenSource(requestCTS.Token, token);
using var response = await _httpClient.SendAsync(request, linkedCts.Token);
if (!response.IsSuccessStatusCode)
{
var error = await response.Content.ReadAsStringAsync(token);
throw new Exception(error);
}
}
private async Task<T> sendRequest<T>(HttpRequestMessage request, int timeOut = 100, CancellationToken token = default)
{
try
{
using var requestCTS = new CancellationTokenSource(TimeSpan.FromSeconds(timeOut));
using var linkedCts = CancellationTokenSource.CreateLinkedTokenSource(requestCTS.Token, token);
using var response = await _httpClient.SendAsync(request, linkedCts.Token);
if (!response.IsSuccessStatusCode)
{
var error = await response.Content.ReadAsStringAsync(token);
throw new Exception(error);
}
return (await response.Content.ReadFromJsonAsync<T>())!;
}
update
private async Task<HttpContent> sendRequest(HttpRequestMessage request, int timeOut = 100, CancellationToken token = default)
{
try
{
using var requestCTS = new CancellationTokenSource(TimeSpan.FromSeconds(timeOut));
using var linkedCts = CancellationTokenSource.CreateLinkedTokenSource(requestCTS.Token, token);
using var response = await _httpClient.SendAsync(request, linkedCts.Token);
if (!response.IsSuccessStatusCode)
{
await response.Content.ReadAsStringAsync(token);
throw new Exception(error);
}
return response.Content;
}
catch (AccessTokenNotAvailableException)
{
_navigationManager.NavigateToLogout(StaticStringHelper.LogoutPatch);
return default(HttpContent)!;
}
}
and when i use it like
public async Task<T> Get<T>(string uri, int timeOut = 100, CancellationToken token = default)
{
var request = new HttpRequestMessage(HttpMethod.Get, uri);
return await (await sendRequest(request, timeOut, token)).ReadFromJsonAsync<T>(cancellationToken: token);
}
i get
microsoft.AspNetCore. Components.WebAssembly.Rendering.WebAssemblyRenderer[100]
Unhandled exception rendering component: Cannot access a disposed object.
Object name: 'System.Net.Http.BrowserHttpContent'.
System. ObjectDisposedException: Cannot access a disposed object.
Object name: 'System.Net.Http.BrowserHttpContent'.
at System.Net.Http.HttpContent.CheckDisposed()
at System.Net.Http.HttpContent.ReadAsStreamAsync(CancellationToken cancellationToken)
答案1
得分: 1
C# 中没有通用的 "void"。在某些情况下,你可以只是使用一个带有像 int 或 bool 这样的无意义值的通用类型。在其他情况下,你只需要接受它并保留两个实现。
在这种特定情况下,唯一的区别似乎是 response.Content.ReadFromJsonAsync<T>()
,因此你可以重构它,使所有其他代码都通用。由于内容已被处理,你需要使用委托来注入不同的代码:
public Task Post(string uri, HttpContent content, int timeOut = 100, CancellationToken token = default)
=> Post<int>(uri, content, c => Task.FromResult(0), timeOut , token);
public async Task<T> Post<T>(string uri, HttpContent content, int timeOut = 100, CancellationToken token = default)
=> Post(uri, content, ReadFromJson, timeOut , token)!;
private Task<T> ReadFromJson(HttpContent content) => content.ReadFromJsonAsync<T>();
private async Task<T> Post<T>(string uri, HttpContent content, Func<HttpContent, Task<T>> handleContent, int timeOut = 100, CancellationToken token = default)
{
var request = new HttpRequestMessage(HttpMethod.Post, uri);
request.Content = content;
try
{
using var requestCTS = new CancellationTokenSource(TimeSpan.FromSeconds(timeOut));
using var linkedCts = CancellationTokenSource.CreateLinkedTokenSource(requestCTS.Token, token);
using var response = await _httpClient.SendAsync(request, linkedCts.Token);
if (!response.IsSuccessStatusCode)
{
var error = await response.Content.ReadAsStringAsync(token);
throw new Exception(error);
}
return await handleContent(response.Content);
}
c => Task.FromResult(0)
只是为了使通用类型适配,'0' 值从未在任何地方使用。
英文:
There is no generic "void" in c#. In some cases you can just use a generic with something like a int or bool as a meaningless value. In other cases you just have to suck it up and keep two implementations.
In this particular case the only difference seem to be response.Content.ReadFromJsonAsync<T>()
, so you should be able to refactor this to make all other code common. Since the content is disposed you need to use a delegate to inject the code that is different:
public Task Post(string uri, HttpContent content, int timeOut = 100, CancellationToken token = default)
=> Post<int>(uri, content, c => Task.FromResult(0), timeOut , token);
public async Task<T> Post<T>(string uri, HttpContent content, int timeOut = 100, CancellationToken token = default)
=> Post(uri, content, ReadFromJson, timeOut , token)!;
private Task<T> ReadFromJson(HttpContent content) => content.ReadFromJsonAsync<T>();
private async Task<T> Post<T>(string uri, HttpContent content, Func<HttpContent, Task<T>> handleContent, int timeOut = 100, CancellationToken token = default)
{
var request = new HttpRequestMessage(HttpMethod.Post, uri);
request.Content = content;
try
{
using var requestCTS = new CancellationTokenSource(TimeSpan.FromSeconds(timeOut));
using var linkedCts = CancellationTokenSource.CreateLinkedTokenSource(requestCTS.Token, token);
using var response = await _httpClient.SendAsync(request, linkedCts.Token);
if (!response.IsSuccessStatusCode)
{
var error = await response.Content.ReadAsStringAsync(token);
throw new Exception(error);
}
return await handleContent(response.Content);
}
c => Task.FromResult(0)
is just something to make the generic types fit, the '0' value never used anywhere.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论