获取对任务状态机的实际 this 引用

Getting the actual this reference to the task state machine

我正在考虑篡改任务内部状态机的状态,但我无法找到在我的任务方法中实际访问状态机引用的方法。

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

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

一种方法是将 IndexAsync class 的引用或实例传递到测试 class。

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

如果您分享更多代码,将更容易确定在您的情况下如何做。

您可以从堆栈上的 class 获取第一个方法调用作为变体:

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

这很糟糕,而且不能保证有效(取决于实现细节)- 但这对我有用……它基本上会激发状态机将延续传递给等待者。然后我们可以从延续委托的目标中获取状态机。

丑陋、丑陋、丑陋的代码……但它确实对我有用:)

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: