获取对任务状态机的实际 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:
我正在考虑篡改任务内部状态机的状态,但我无法找到在我的任务方法中实际访问状态机引用的方法。
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: