2017-02-17 62 views
3

我在玩捣乱任务内部状态机状态的想法,但我很难找到一种方法来实际访问我内部的状态机参考任务方法。获取实际参考任务状态机

class Test 
{ 
    async Task IndexAsync() 
    { 
     var nottheactualtype = GetType(); //This references the "Test" class, but this operation is actually located in the nested state machine class named "IndexAsync", in the method "MoveNext()". 
     var actualcalledmethod = new StackTrace().GetFrame(0).GetMethod(); //This shows the actual method currently being run: IndexAsync.MoveNext(). 
     //But how do I get the reference to my current IndexAsync class? 
    } 
} 

我该如何访问当前运行的生成状态机的引用?

+0

是否必须是方法本身?我有很多代码在https://github.com/jskeet/DemoCode/tree/master/Abusing%20CSharp/Code/FunWithAwaiters做这种事情可能会帮助你,但它并不倾向于获得状态机器*内*异步方法。 –

+0

我相信如此。在异步方法中的某个位置,我想查看状态机中的哪个状态正在运行。除此之外还有更多,但其实质是我想访问当前正在运行的状态机的成员。我可以通过反射访问成员,并且可以访问生成的类的类型。但我似乎无法找到访问实际引用的方法。 – Micael

+0

好吧,我想我已经想出了一些东西,但我需要尝试一下...... –

回答

0

你可以把第一个方法调用从你的类上堆的变种:

var nottheactualtype = GetType(); 
var actualcalledmethod = new StackTrace().GetFrames().FirstOrDefault(x => x.GetMethod().ReflectedType == nottheactualtype); 
+0

对不起,但是它给出了与'this.GetType()'相同的结果,这不是我所追求的。问题是编译器会隐藏“this”引用以隐藏编译器生成的状态机类。使用StackTrace,我可以访问状态机类的类型实例,但我需要访问实际运行的实例。 – Micael

0

一种方法是将IndexAsync类的引用或实例传递给Test类。

另一种选择是通过反射将它分配给一个私人支持字段,但我更喜欢第一个选项,因为反射可能很慢。

如果您分享了更多的代码,将会更容易确定如何处理您的案例。

+0

没有抱歉,它不起作用。嵌套的IndexAsync类是编译器生成的([本博客条目很好地解释了异步任务的内部工作原理:[link](https://weblogs.asp.net/dixin/understanding-c-sharp-async-await -1-compilation)),所以我只能在运行时访问它。 – Micael

1

这是讨厌的,而且它不能保证工作(这取决于实施细则) - 但是这对我的作品...它基本上引起状态机将延续传递给服务员。然后我们可以让状态机脱离继续代理的目标。

丑陋的,丑陋的,丑陋的代码......但它确实为我工作:)

using System; 
using System.Reflection; 
using System.Threading.Tasks; 
using System.Runtime.CompilerServices; 
using static System.Reflection.BindingFlags; 

public class StateMachineProvider 
{ 
    private static readonly StateMachineProvider instance = new StateMachineProvider(); 

    public static StateMachineProvider GetStateMachine() => instance; 

    public StateMachineAwaiter GetAwaiter() => new StateMachineAwaiter(); 

    public class StateMachineAwaiter : INotifyCompletion 
    { 
     private Action continuation; 

     public bool IsCompleted => continuation != null; 

     public void OnCompleted(Action continuation) 
     { 
      this.continuation = continuation; 
      // Fire the continuation in a separate task. 
      // (We shouldn't just call it synchronously.) 
      Task.Run(continuation); 
     } 

     public IAsyncStateMachine GetResult() 
     { 
      var target = continuation.Target; 
      var field = target.GetType() 
           .GetField("m_stateMachine", NonPublic | Instance); 
      return (IAsyncStateMachine) field.GetValue(target); 
     } 
    } 
} 

class Test 
{ 
    static void Main() 
    { 
     AsyncMethod().Wait(); 
    } 

    static async Task AsyncMethod() 
    { 
     int x = 10; 
     IAsyncStateMachine machine = await StateMachineProvider.GetStateMachine(); 
     Console.WriteLine($"x={x}"); // Force the use across an await boundary 
     Console.WriteLine($"State machine type: {machine.GetType()})"); 
     Console.WriteLine("Fields:"); 
     var fields = machine.GetType().GetFields(Public | NonPublic | Instance); 
     foreach (var field in fields) 
     { 
      Console.WriteLine($"{field.Name}: {field.GetValue(machine)}"); 
     } 
    } 
} 

输出:

x=10 
State machine type: Test+<AsyncMethod>d__1) 
Fields: 
<>1__state: -1 
<>t__builder: System.Runtime.CompilerServices.AsyncTaskMethodBuilder 
<x>5__1: 10 
<machine>5__2: Test+<AsyncMethod>d__1 
<>s__3: 
<>s__4: System.Reflection.FieldInfo[] 
<>s__5: 6 
<field>5__6: System.Reflection.FieldInfo <field>5__6 
<>u__1: 
+0

我同意继续的力量是相当丑陋的,但这种方法是辉煌的,绝对值得一试。谢谢! – Micael