2017-02-17 46 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