14

我正在覆盖基类库中的方法。但是,在我重写的实现中,我使用了新的 HttpClient,它全部基于异步方法。因此,我必须将我的方法标记为异步,这意味着我需要将方法的返回参数从字符串更改为任务。然而,编译器给出了一个错误:“返回类型必须是'字符串'才能匹配被覆盖的成员......”

    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<string>,但我无法控制 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;
        }
    }

有没有其他方法可以做到这一点?

4

3 回答 3

10

不幸的是,这里没有一个好的解决方案。没有override异步方法的非异步方法。我认为你最好的选择是有一个async非覆盖方法并从非异步方法中调用它:

public class MyClass : BaseClass 
{
    public override string GetName() 
    {
        return GetNameAsync().Value;
    }

    public async Task<string> GetNameAsync() 
    { 
        ...
    }
}

请注意,这可能会导致问题。如果原始代码不期望任何async代码正在执行,则引入此模式可能会打破预期。如果可能的话,我会避免它。

于 2013-03-10T01:44:15.587 回答
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.

于 2016-12-22T12:13:31.710 回答
-5

我也遇到过这个问题,解决方案是使用一个接口,其中“异步”不是方法签名的一部分。

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
    }
}
于 2013-05-31T13:52:32.910 回答