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 に答える