You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在C# 5.0中实现JS的for await...of与async*异步生成器?

C# 5.0 等效实现老虎机spin与infiniteSpinning函数

由于C# 5.0(.NET Framework 4.8)不支持C# 8.0引入的异步迭代器(IAsyncEnumerable),无法直接对应JS的async*生成器与for await...of语法,需要通过回调模式或异步任务序列来模拟核心逻辑。以下是针对老虎机卷轴旋转功能的完整实现方案:

前置定义

先模拟原JS中涉及的核心类型(需根据实际项目调整):

// 存储卷轴每帧的偏移量
public struct ReelOffset
{
    public double VerticalOffset { get; set; }
}

// 卷轴类,包含原JS中旋转更新与目标位置检查逻辑
public class Reel
{
    // 模拟原JS的updateSpin方法:计算每帧旋转偏移量
    public ReelOffset UpdateSpin()
    {
        // 此处替换为原JS中的旋转计算逻辑
        return new ReelOffset { VerticalOffset = /* 动态计算的偏移值 */ };
    }

    // 模拟原JS的isAtTarget方法:检查是否到达目标位置
    public bool IsAtTarget(int targetPosition)
    {
        // 此处替换为原JS中的目标位置判断逻辑
        return false;
    }
}

方案一:回调+异步循环(推荐,适配WPF UI更新)

该方案通过回调函数处理每帧旋转数据,用CancellationToken控制循环终止,完美匹配原JS生成器的"持续产生值直到停止"逻辑:

等效infiniteSpinning的实现

/// <summary>
/// 持续生成卷轴旋转偏移量,直到收到取消信号
/// </summary>
/// <param name="reels">所有卷轴实例</param>
/// <param name="cancellationToken">取消令牌,用于终止旋转</param>
/// <param name="onFrameUpdate">每帧更新的回调,处理UI刷新</param>
/// <returns></returns>
public async Task InfiniteSpinning(IEnumerable<Reel> reels, CancellationToken cancellationToken, Action<ReelOffset[]> onFrameUpdate)
{
    while (!cancellationToken.IsCancellationRequested)
    {
        // 计算所有卷轴当前帧的偏移量
        var frameOffsets = reels.Select(reel => reel.UpdateSpin()).ToArray();
        
        // 触发回调更新UI(WPF需确保在Dispatcher线程执行)
        Application.Current.Dispatcher.Invoke(() => onFrameUpdate(frameOffsets));
        
        // 模拟JS的requestAnimationFrame:等待约16ms(60帧/秒)
        await Task.Delay(TimeSpan.FromMilliseconds(16), cancellationToken);
    }
}

等效spin的实现

/// <summary>
/// 启动卷轴旋转,直到所有卷轴到达目标位置
/// </summary>
/// <param name="reels">所有卷轴实例</param>
/// <param name="targetPositions">每个卷轴的目标位置数组</param>
/// <returns></returns>
public async Task Spin(IEnumerable<Reel> reels, int[] targetPositions)
{
    using var cancellationSource = new CancellationTokenSource();
    
    // 启动无限旋转任务
    var spinningTask = InfiniteSpinning(reels, cancellationSource.Token, offsets =>
    {
        // 此处替换为实际的UI更新逻辑:根据偏移量调整卷轴位置
        // 例如:foreach (var (reel, offset) in reels.Zip(offsets)) { ... }
    });

    // 循环检查是否到达停止条件
    while (true)
    {
        // 避免高频检查,等待一帧后再判断
        await Task.Delay(16);
        
        // 验证所有卷轴是否到达目标位置
        var allReelsAtTarget = reels.Zip(targetPositions, (reel, target) => reel.IsAtTarget(target))
                                    .All(isAtTarget => isAtTarget);
        
        if (allReelsAtTarget)
        {
            // 触发旋转终止
            cancellationSource.Cancel();
            break;
        }
    }

    // 等待旋转任务完成(忽略主动取消引发的异常)
    try
    {
        await spinningTask;
    }
    catch (OperationCanceledException) { }
}

方案二:异步任务序列模拟迭代

若更倾向于接近JS迭代器的写法,可通过IEnumerable<Task<ReelOffset[]>>生成异步任务序列,手动遍历并等待每个任务结果:

等效infiniteSpinning的实现

/// <summary>
/// 生成包含每帧偏移量的异步任务序列
/// </summary>
/// <param name="reels">所有卷轴实例</param>
/// <param name="cancellationToken">取消令牌</param>
/// <returns></returns>
public IEnumerable<Task<ReelOffset[]>> GenerateSpinningSequence(IEnumerable<Reel> reels, CancellationToken cancellationToken)
{
    while (!cancellationToken.IsCancellationRequested)
    {
        // 生成当前帧的偏移量任务
        yield return Task.Run(() => reels.Select(reel => reel.UpdateSpin()).ToArray(), cancellationToken);
    }
}

等效spin的实现

public async Task Spin(IEnumerable<Reel> reels, int[] targetPositions)
{
    using var cancellationSource = new CancellationTokenSource();
    var spinningSequence = GenerateSpinningSequence(reels, cancellationSource.Token);

    foreach (var frameTask in spinningSequence)
    {
        if (cancellationSource.Token.IsCancellationRequested) break;

        // 等待当前帧偏移量计算完成
        var offsets = await frameTask;
        
        // 更新UI(需在Dispatcher线程执行)
        Application.Current.Dispatcher.Invoke(() =>
        {
            // 替换为实际UI更新逻辑
        });

        // 检查停止条件
        var allAtTarget = reels.Zip(targetPositions, (reel, target) => reel.IsAtTarget(target))
                               .All(b => b);
        if (allAtTarget)
        {
            cancellationSource.Cancel();
            break;
        }

        // 等待下一帧
        await Task.Delay(16, cancellationSource.Token);
    }
}

关键注意事项

  1. WPF线程安全:所有UI操作必须在Dispatcher线程执行,因此回调或UI更新代码需用Application.Current.Dispatcher.Invoke包裹。
  2. 帧同步:用Task.Delay(16)模拟JS的requestAnimationFrame,若需要更精确的帧同步,可改用CompositionTarget.Rendering事件。
  3. 取消逻辑:通过CancellationTokenSource主动终止旋转循环,避免内存泄漏或无效计算。

内容的提问来源于stack exchange,提问作者ImUseless

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.23 05:57:17