2017-02-17 47 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()', что совсем не то, что мне нужно. Проблема в том, что компилятор скрывает «эту» ссылку, чтобы скрыть класс конечного автомата, созданный компилятором. Используя StackTrace, я могу получить доступ к экземпляру типа класса конечного автомата, но мне нужен доступ к фактическому запущенному экземпляру. – Micael

0

Один из способов сделать это - передать ссылку или экземпляр вашего класса IndexAsync в класс Test.

Другой вариант - назначить его частному полю поддержки через отражение, хотя я бы предпочел первый вариант, так как отражение может быть медленным.

Если вы разделите больше своего кода, будет легче определить, как это сделать в вашем случае.

+0

Ничего страшного, это не работает. Вложенный класс IndexAsync генерируется компилятором ([в этой записи в блоге хорошо объясняется, как работают внутренние задачи async: [link] (https://weblogs.asp.net/dixin/understanding-c-sharp-async-await -1-компиляция)), поэтому я могу получить доступ к нему только во время выполнения. – Micael

1

Это противно, и это не гарантирует работу (это зависит от деталей реализации) - но это работает для меня ... он в основном провоцирует конечный автомат, чтобы передать продолжение на awaiter. Затем мы можем вывести государственный автомат из цели делегата продолжения.

Гадкий, уродливый, некрасивый код ... но это не работа для меня :)

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

 Смежные вопросы

  • Нет связанных вопросов^_^