Return Task.ContinueWith <TResult> () without TResult knowledge

How to create and return a continuation task using reflection or in any other way if I only have access to the Task?

I need the continuation exception to return to the original caller. As far as I know, this can only be done by returning the continue task instead of the original task. The problem is that I don’t know the type of the result of the task, so I can’t create the proper continuation task.

Edit: I cannot change signature types. I have many interfaces that return a Task <TResult> and cannot expect the client to get the Task <Object> results. These interfaces are WCF contracts. I want to perform some additional validation logic after the "main" logical method has been executed, and, if necessary, cross out the exception. This exception should return to the client, but it is not returning now, because I am not returning the continuation task yet. Also, I don’t know the type in advance, because I apply the postsharp aspect and using the OnExit () override, it gives me access to the return value, which, as I know, is the Task, but it can be any number of task objects from which TResult is known only at runtime.

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

Another way to express a problem.

  • I can only change what is inside DoExtraValidation ().
  • I cannot change the signature of DoExtraValidation () to use generics.

How to change DoExtraValidation so that it works for any task <TResult> return type?

 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(); } } } 
+4
source share
2 answers

Sounds like a case for dynamic . I will try to use as little dynamic as possible. First, we define a strongly typed helper:

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

This function obviously works, but it requires TResult . dynamic can populate it:

 Task continuation = SetContinuation((dynamic)unknownReturnType); 

I just checked that the binding works at runtime. Alternatively, you can use reflection instead of calling the helper (use MethodInfo.MakeGenericMethod and others).

+5
source

You can use generics (Interceptor is an extension method):

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

Thus, the call can be of any type:

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

Source: https://habr.com/ru/post/1490917/


All Articles