2013-07-11 41 views
4

如何創建並返回繼續任務使用反射或通過任何其他方式,如果我只有訪問任務?返回Task.ContinueWith <TResult>()不知道TResult

我需要一種方法來讓繼續中的異常傳回原始調用者。據我所知,這隻能通過返回繼續任務而不是原始任務來完成。問題在於我不知道任務的結果類型,因此無法創建適當的延續任務。

編輯: 我無法更改簽名類型。我有很多接口返回任務< TResult>對象,並且不能指望客戶端獲取任務<對象>結果。這些接口是WCF合同。我想在「核心」邏輯方法完成後再做一些額外的驗證邏輯,並在需要時拋出異常。這個例外必須返回給客戶端,但目前不能返回,因爲我還沒有返回延續任務。此外,由於我應用Postharp方面並使用OnExit()重寫,因此我不知道該類型,因爲這使我可以訪問返回值,我知道這是一個Task,但它可以是任意數量的Task對象,其中TResult只在運行時才知道。

using System; 
using System.Threading.Tasks; 

    namespace TaskContinueWith 
    { 
     internal class Program 
     { 
      private static void Main(string[] args) 
      { 
       try 
       { 
        Task<string> myTask = Interceptor(); 
        myTask.Wait(); 
       } 
       catch (Exception ex) 
       { 
        Console.WriteLine(ex); 
       } 

       Console.ReadLine(); 
      } 

      private static Task<string> Interceptor() 
      { 
       Task<string> task = CoreLogic(); //Ignore 


       Task unknownReturnType = task; //This is what I have access to. A Task object which can be one of numerous Task<TResult> types only known at runtime. 

       Task continuation = unknownReturnType.ContinueWith(
        t => 
         { 
         if(someCondition) 
         { 
          throw new Exception("Error"); 
         } 

         return t.Result; //This obviously does not work since we don't know the result. 
        }); 

       return continuation; 
      } 


      private static async Task<string> CoreLogic() 
      { 
       return "test"; 
      } 
     } 
    } 

表達問題的另一種方式。

  1. 我只能改變DoExtraValidation()裏面的內容。
  2. 我無法更改DoExtraValidation()的簽名以使用泛型。

如何更改DoExtraValidation以使其適用於任何任務< TResult>返回類型?

using System; 
using System.Threading.Tasks; 

namespace TaskContinueWith 
{ 
    interface IServiceContract 
    { 
     Task<string> DoWork(); 
    } 

    public class Servce : IServiceContract 
    { 
     public Task<string> DoWork() 
     { 
      var task = Task.FromResult("Hello"); 
      return (Task<string>) DoExtraValidation(task); 
     } 

     private static Task DoExtraValidation(Task task) 
     { 
      Task returnTask = null; 
      if (task.GetType() == typeof(Task<string>)) 
      { 
       var knownType = task as Task<string>; 
       returnTask = task.ContinueWith(
        t => 
        { 
         if(new Random().Next(100) > 50) 
         { 
          throw new Exception("Error"); 
         } 
         return knownType.Result; 

        }); 
      } 
      return returnTask; 
     } 
    } 

    internal class Program 
    { 
     private static void Main(string[] args) 
     { 
      try 
      { 
       IServiceContract myService = new Servce(); 
       Task<string> myTask = myService.DoWork(); 
       myTask.Wait(); 
      } 
      catch (Exception ex) 
      { 
       Console.WriteLine(ex); 
      } 

      Console.ReadLine(); 
     } 
    } 
} 
+0

爲了讓你的榜樣工作,unknownReturnType任務必須是'Task ',因爲這是你的'Interceptor()'方法返回的。它總是一個任務'?如果是這樣,那麼演員陣容將工作。如果沒有,那麼你可以返回任務然後客戶端代碼需要弄清楚(不知何故)它回來的是什麼。但我的問題是 - 爲什麼你不知道正在返回的類型?最好能更好地描述你想要做什麼 - 可能有更好的解決方案。 –

回答

5

聽起來像是dynamic的情況下。我會盡量少用dynamic。首先我們定義一個強類型的幫手:

static Task<TResult> SetContinuation<TResult>(Task<TResult> task) 
{ 
    return task.ContinueWith(
     t => 
      { 
      if(someCondition) 
      { 
       throw new Exception("Error"); 
      } 

      return t.Result; 
     }); 
} 

此功能明顯的作品,但它需要TResult是已知的。 dynamic可以填寫它:

Task continuation = SetContinuation((dynamic)unknownReturnType); 

我剛剛測試過綁定在運行時工作。或者,您可以使用反射來調用助手(使用MethodInfo.MakeGenericMethod等)。

+0

我做了一些更改,但似乎拋出以下內容。 Microsoft.CSharp.RuntimeBinder.RuntimeBinderException:在CallSite.Target(Closure)上爲非靜態字段,方法或屬性'TaskContinueWith.Program.S etContinuation (System.Threading.Task.Task )'請求對象引用,CallSite,類型,對象)在System.Dynamic.UpdateDelegates.UpdateAndExecute2 [T0,T1,TRet](CallSite站點 – Tersius

+0

我使該方法成爲靜態以解決錯誤。回答您的其他評論:關鍵是'dynamic'它內部確實使用了反射!它會在運行時填寫類型參數。試試吧 – usr

+0

是的,它在最後工作:)問題。這將是最快的。 1.使用動態調用SetContinuation。或者2.使用compileTimeGenerated MethodInfo對象並調用invoke?我認爲2應該更快沒有?我想問的原因是我可以使用Resharper的compileTime magic爲我生成一個通用方法,因此它可以節省運行時間。 – Tersius

1

你可以使用泛型(Interceptor是這裏的擴展方法):

public static class ProjectExtensions 
{ 
    public static Task<T> Interceptor<T>(this Task<T> task) 
    { 
     Task<T> continuation = task.ContinueWith<T>(t => 
      { 
      if(someCondition) 
      { 
       throw new Exception("Error"); 
      } 
      return t.Result; 
     }); 
     return continuation; 
    } 
} 

這樣一個電話可以是任何類型的:

var t1 = new Task<string>(() => /* return string */).Interceptor(); 
var t2 = new Task<int>(() => /* return int */).Interceptor(); 
+0

對不起。我移動它。無論如何。我不明白這是如何工作的,因爲它需要將一些類型信息傳遞給擴展方法。我沒有這種類型的信息。我只能用反射。我將如何使用反射調用此方法以允許類型信息通過? – Tersius

+0

@Tersius,這個解決方案對於你給出的第二個例子是正確的(因爲在這個例子中調用DoValidation時,已知你正在處理任務)。如果您想要更好的示例,請向我們展示一個簡潔但真實的示例,其中顯示方法DoExtraValidation不知道類型信息的位置。 –

相關問題