【问题标题】:F# async/await in a viewmodelF# async/await 在视图模型中
【发布时间】:2016-10-27 11:23:02
【问题描述】:

我在 F# 中的 ViewModel

我正在尝试使用 F# 而不是 C# 来实现我的 ViewModel。 我正在关注这个article(顺便说一句,有没有更新或更好的建议?)。

假设我有我的视图模型基础实现(MVVM.ViewModel,它在 C# 中,但我可以从 F# 中引用它)和一个简单的 Status 属性。

namespace FuncViewModel
open MVVM.ViewModel
open System

    type MyFuncViewModel() = 
        inherit ViewModelBase()

        let mutable status=""

        member this.RunSetStatus() =
            status <- "Reset @" + DateTime.Now.ToString "yy.MM.dd hh:mm:ss"
            base.OnPropertyChanged("Status")

        member this.SetStatus = new DelegateCommand(fun _ -> this.RunSetStatus() )


    member this.Status 
        with get() =
            status
        and set(value) =
             status <- value
             base.OnPropertyChanged(fun () -> this.Status)

一切都按预期工作,到目前为止一切都很好(但如果您发现任何概念性错误或者您是否找到上述代码的更惯用的版本,请告诉我)

介绍异步/等待模式

这是我出错的地方:我知道如何在 C# 中做到这一点,但我在 F# 中并不擅长。

我已经尝试过以下方法。

member this.RunSetStatus() =
    status <- "Start resetting @" + DateTime.Now.ToString "yy.MM.dd hh:mm:ss"
    base.OnPropertyChanged("Status")
    let task = async {

        do! Async.Sleep (30 * 1000) 

    }
    Async.StartImmediate(task)
    status <- "Reset done @" + DateTime.Now.ToString "yy.MM.dd hh:mm:ss"
    base.OnPropertyChanged("Status")

问题是 - 当我运行完整的 WPF 应用程序时 - 我看不到预期的延迟:最终状态直接进入输出。

如果我将上面的Async.StartImmediate(task) 更改为Async.RunSynchronously(task),我当然会看到正在进行的延迟,但是应用程序被冻结了,所以这不是我想要的。

如果我把它重新排列成

member this.RunSetStatus() =
    status <- "Start resetting @" + DateTime.Now.ToString "yy.MM.dd hh:mm:ss"
    base.OnPropertyChanged("Status")
    let task = async {

        do! Async.Sleep (30 * 1000) 

        status <- "Reset done @" + DateTime.Now.ToString "yy.MM.dd hh:mm:ss"
        base.OnPropertyChanged("Status")

    }
    Async.StartImmediate(task)

我收到一个错误

成员或对象构造函数 'OnPropertyChanged' 不是 无障碍。私有成员只能从内部访问 声明类型。受保护的成员只能从 扩展类型,不能从内部 lambda 表达式访问。

编辑(续)

最后我也试过了

member this.RunSetStatus() =
    status <- "Start resetting @" + DateTime.Now.ToString "yy.MM.dd hh:mm:ss"
    base.OnPropertyChanged("Status")
    let task = async {

        do! Async.Sleep (30 * 1000) 

    }
    Async.StartWithContinuations(task, 
        (fun _ -> this.Status <- "Reset done @" + DateTime.Now.ToString "yy.MM.dd hh:mm:ss"),
        (fun _ -> this.Status <- "Operation failed."),
        (fun _ -> this.Status <- "Operation canceled."))

但应用程序崩溃并出现 ArgumentException

堆栈跟踪

应用程序:MyFuncWPF.exe 框架版本:v4.0.30319 描述: 由于未处理的异常,该进程被终止。例外 信息:System.ArgumentException 堆栈:在 MVVM.ViewModel.ViewModelBase.OnPropertyChanged[System.__Canon, mscorlib,版本=4.0.0.0,文化=中性, PublicKeyToken=b77a5c561934e089] 在 FuncViewModel.MyFuncViewModel.set_Status(System.String) 在 .$MyVMLib+RunSetStatus@20.Invoke(Microsoft.FSharp.Core.Unit) 在 Microsoft.FSharp.Control.CancellationTokenOps+StartWithContinuations@1274[[System.__Canon, mscorlib,版本=4.0.0.0,文化=中性, PublicKeyToken=b77a5c561934e089]].Invoke(System.__Canon) 在 .$Control.loop@430-52(Microsoft.FSharp.Control.Trampoline, Microsoft.FSharp.Core.FSharpFunc2<Microsoft.FSharp.Core.Unit,Microsoft.FSharp.Control.FakeUnitValue>) at Microsoft.FSharp.Control.Trampoline.ExecuteAction(Microsoft.FSharp.Core.FSharpFunc2) 在 .$Control+-ctor@507.Invoke(System.Object) 在 System.Windows.Threading.ExceptionWrapper.InternalRealCall(System.Delegate, 系统对象

编辑 2 - 发现问题

我不得不使用以下更简单的OnPropertyChanged 重载(根据source code,它们都在 C# 中实现和工作)

    member this.Status 
        with get() =
            status
        and set(value) =
             status <- value
             base.OnPropertyChanged("Status")

【问题讨论】:

标签: mvvm f# async-await fsharp.viewmodule


【解决方案1】:

异常原因是F#的函数不能表示为MemberExpression:

    protected virtual void OnPropertyChanged<T>(Expression<Func<T>> selectorExpression)
    {
        if (selectorExpression == null)
            throw new ArgumentNullException("selectorExpression");
        MemberExpression body = selectorExpression.Body as MemberExpression;
        if (body == null)
            throw new ArgumentException("The body must be a member expression");
        OnPropertyChanged(body.Member.Name);
    }

在调试器中,您会看到您得到的异常实际上是 - “主体必须是成员表达式”。

你的第一个代码:

member this.RunSetStatus() =
    status <- "Reset @" + DateTime.Now.ToString "yy.MM.dd hh:mm:ss"
    base.OnPropertyChanged("Status")

因为您没有使用属性 Status 的设置器而有效。

所以你需要使用不同的重载。

【讨论】:

  • @GoFunctional 不考虑各种 C# 互操作问题,是否有理由不使用普通的旧 Async.Start?
  • @Go Functional 如果您有任何问题,请随时提问
猜你喜欢
  • 2017-02-18
  • 2020-06-11
  • 1970-01-01
  • 2020-03-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多