加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 编程开发 > Java > 正文

mvvm – 视图模型中的F#async / await

发布时间:2020-12-15 02:17:16 所属栏目:Java 来源:网络整理
导读:我在F#中的ViewModel 我正在尝试使用F#而不是C#来实现我的ViewModel. 我正在关注这个article(顺便问一下,有什么新的或更好的建议吗?). 所以,假设我有我的视图模型库实现(MVVM.ViewModel,它在C#中,但我可以从F#引用它)和一个简单的Status属性. namespace Fun
我在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)

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

介绍async / await模式

这就是我出错的地方:我知道如何在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(任务)更改为Async.RunSynchronously(任务),当然我看到正在进行的延迟,但应用程序被冻结,所以这不是我想要的.

如果我将其重新排列为

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)

我收到一个错误

The member or object constructor ‘OnPropertyChanged’ is not
accessible. Private members may only be accessed from within the
declaring type. Protected members may only be accessed from an
extending type and cannot be accessed from inner lambda expressions.

编辑(续)

最后,我也试过这个

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

堆栈跟踪

Application: MyFuncWPF.exe Framework Version: v4.0.30319 Description:
The process was terminated due to an unhandled exception. Exception
Info: System.ArgumentException Stack: at
MVVM.ViewModel.ViewModelBase.OnPropertyChanged[System.__Canon,
mscorlib,Version=4.0.0.0,Culture=neutral,
PublicKeyToken=b77a5c561934e089]
at FuncViewModel.MyFuncViewModel.set_Status(System.String) at
.$MyVMLib+RunSetStatus@20.Invoke(Microsoft.FSharp.Core.Unit)
at
Microsoft.FSharp.Control.CancellationTokenOps+StartWithContinuations@1274[[System.__Canon,
PublicKeyToken=b77a5c561934e089]].Invoke(System.__Canon) at
.$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.FSharpFunc
2)
at .$Control+-ctor@507.Invoke(System.Object)
at
System.Windows.Threading.ExceptionWrapper.InternalRealCall(System.Delegate,
System.Object

编辑2 – 发现问题

我不得不使用以下内容 – 更简单 – OnPropertyChanged的重载(它们都是按照这个source code实现并在C#中工作)

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

解决方法

异常的原因是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")

因为您没有使用属性设置器状态,所以可以正常工作.

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

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读