我正在覆盖基类库中的方法。但是,在我重写的实现中,我正在使用全新的HttpClient,它全部基于异步方法。因此我必须将我的方法标记为异步,这意味着我需要将方法的返回参数从字符串更改为任务。然而,编译器会给出一个错误:“返回类型必须是'string'才能匹配被覆盖的成员....”
public class BaseClass
{
public virtual string GetName()
{
...
}
}
public class MyClass : BaseClass
{
public override async Task<string> GetName()
{
HttpClient httpClient = new HttpClient();
var response = await httpClient.GetAsync("");
if (response.IsSuccessStatusCode)
{
var responseContent = response.Content;
return await responseContent.ReadAsStringAsync();
}
return null;
}
}
当然,显而易见的解决方案是将BaseClass中GetName()的返回类型更改为Task&lt; string&gt;,但我无法控制BaseClass,因为它是外部库;
我目前的解决方案是以同步方式使用HttpClient类,即按如下方式更改MyClass:
public class MyClass : BaseClass
{
public override string GetName()
{
HttpClient httpClient = new HttpClient();
var response = httpClient.GetAsync("");
if (response.Result.IsSuccessStatusCode)
{
var responseContent = response.Result.Content;
return responseContent.ReadAsStringAsync()
.Result;
}
return null;
}
}
还有其他办法吗?
答案 0 :(得分:10)
不幸的是,这里没有一个好的解决方案。没有办法override
使用异步方法的非异步方法。我认为你最好的选择是使用async
非覆盖方法并从非异步方法中调用它:
public class MyClass : BaseClass
{
public override string GetName()
{
return GetNameAsync().Value;
}
public async Task<string> GetNameAsync()
{
...
}
}
请注意,这可能会导致问题。如果原始代码没有期望任何async
代码执行,那么引入这种模式可能会打破期望。如果可能的话我会避免它。
答案 1 :(得分:0)
幸运的是,ReadAsStringAsync().Result
并未造成死锁,因为它可能会有ConfigureAwait(false)
。
为防止死锁,您可以使用以下方法之一:
public static T GetResult<T>(Func<Task<T>> func)
{
var httpContext = HttpContext.Context;
var proxyTask = Task.Run(() =>
{
HttpContext.Context = httpContext;
return func();
});
return proxyTask.Result;
}
// or
public static T GetResult<T>(Func<Task<T>> func)
{
var syncContext = SynchronizationContext.Current;
SynchronizationContext.SetSynchronizationContext(null);
var task = func();
SynchronizationContext.SetSynchronizationContext(syncContext);
return task.Result;
}
这样你就可以打电话了
public override string GetName()
{
...
return GetResult(() => responseContent.ReadAsStringAsync());
...
}
前者通过产生新线程而具有性能开销,而后者遭受破坏SynchronizationContext
流,这使得绑定到它的任何上下文在被调用的任务中不可用,例如, HttpContext.Current
。
答案 2 :(得分:-5)
我也有这个问题,解决方案是使用一个接口,其中'async'不是方法签名的一部分。
public abstract class Base : IInvokable {
/* Other properties ... */
public virtual async Task Invoke() {
/*...*/
}
}
public interface IInvokable {
Task Invoke();
}
public class Derived
{
public override async Task Invoke() {
// Your code here
}
}