2013-03-10 30 views
14

Tôi đang ghi đè phương thức trong thư viện lớp cơ sở. Tuy nhiên, bên trong thực hiện ghi đè của tôi, tôi đang sử dụng HttpClient mới, tất cả đều dựa trên các phương thức async. Do đó, tôi phải đánh dấu phương thức của tôi là async, có nghĩa là tôi cần phải thay đổi tham số trả về của phương thức từ chuỗi thành Task. Tuy nhiên trình biên dịch đưa ra một lỗi: "Các kiểu trả về phải là 'chuỗi' để phù hợp với thành viên ghi đè ...."Thay đổi thành viên bị ghi đè thành không đồng bộ

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; 
     } 
    } 

Tất nhiên là giải pháp hiển nhiên sẽ là thay đổi kiểu trả về của getName() trong BaseClass vào Task < chuỗi >, nhưng tôi không kiểm soát được BaseClass vì nó là một thư viện bên ngoài;

giải pháp hiện tại của tôi là sử dụng các lớp HttpClient một cách đồng bộ, ví dụ: MyClass thay đổi như sau:

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; 
     } 
    } 

Có cách nào khác để làm điều này?

+0

Thật không may, không có giải pháp tốt (điều này vi phạm LSP). Bạn có thể tạo một trình bao bọc không đồng bộ không? – SLaks

Trả lời

10

Rất tiếc, không có giải pháp tốt ở đây. Không có cách nào để override một phương pháp không đồng bộ hóa với một phương thức không đồng bộ. Tôi nghĩ rằng đặt cược tốt nhất của bạn là phải có một phương pháp async không ghi đè lên và gọi vào mà từ phi async một:

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

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

Lưu ý rằng điều này có thể gây ra vấn đề mặc dù. Nếu mã ban đầu không mong đợi cho bất kỳ mã async nào để thực thi giới thiệu mẫu này có thể làm hỏng kỳ vọng. Tôi sẽ tránh nó nếu có thể.

+4

Không phải giải pháp này là một bế tắc đang chờ xảy ra? –

+1

@ G.Stoynev vâng, điều đó chắc chắn có thể xảy ra. Như tôi đã chỉ ra đây là một giải pháp có vấn đề và một cái gì đó tôi sẽ tránh ở tất cả có thể. – JaredPar

+0

Cảm ơn bạn @JaredPar. Tôi hiểu rằng nó là vấn đề và sẽ đi cẩn thận :) –

-5

Tôi cũng gặp sự cố này và giải pháp đang sử dụng giao diện, trong đó 'không đồng bộ' không phải là một phần của chữ ký của phương thức.

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 
    } 
} 
+0

OP bang _ "Tất nhiên giải pháp rõ ràng sẽ là thay đổi kiểu trả về của GetName() trong BaseClass thành Task , nhưng ** Tôi không kiểm soát được BaseClass vì nó là thư viện bên ngoài **" _ – MickyD

0

May mắn là ReadAsStringAsync().Result không gây ra bế tắc vì có khả năng có ConfigureAwait(false) bên trong.

Để ngăn chặn một bế tắc, bạn có thể sử dụng một trong các phương pháp sau:

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; 
} 

Bằng cách này bạn sẽ gọi

public override string GetName() 
{ 
    ... 
    return GetResult(() => responseContent.ReadAsStringAsync()); 
    ... 
} 

Các cựu có một buổi biểu diễn trên không bằng cách sinh ra một luồng mới, trong khi sau đó bị phá vỡ SynchronizationContext luồng, làm cho bất kỳ ngữ cảnh nào bị ràng buộc với nó không có sẵn trong nhiệm vụ được gọi, ví dụ: HttpContext.Current.

Các vấn đề liên quan