【问题标题】:How to use a C# task in an F# Xamarin.Forms app?如何在 F# Xamarin.Forms 应用程序中使用 C# 任务?
【发布时间】:2020-05-01 02:40:39
【问题描述】:

我想学习一些 F#,所以我决定用它创建一个 Xamarin.Forms 应用程序,但我对 F# 中的异步和函数式编程非常不熟悉。我正在使用 Xam.Plugins.Media C# 库尝试拍照,但 CrossMedia.Current.TakePhotoAsync() 的行为与我在 C# 中使用它的行为不同。

这是我在 F# 中的内容:

    type MainPage() =
        inherit ContentPage()
        let _ = base.LoadFromXaml(typeof<MainPage>)

        let viewModel = base.BindingContext :?> MainViewModel
        let checkNull (x : 'T) =
            match x with 
            | null -> raise (NullReferenceException(String.Format("{0} is null", x)))
            | _ -> x

        member this.TakePhoto(sender : obj, e : EventArgs) =
            if not CrossMedia.Current.IsCameraAvailable || not CrossMedia.Current.IsTakePhotoSupported then
                raise (Exception("Camera not supported"))
            else 
                let mediaOptions = 
                    Plugin.Media.Abstractions.StoreCameraMediaOptions(
                        Directory = "Test",
                        SaveToAlbum = true,
                        CompressionQuality = 75,
                        CustomPhotoSize = 50,
                        PhotoSize = PhotoSize.MaxWidthHeight,
                        MaxWidthHeight = Nullable(2000),
                        DefaultCamera = CameraDevice.Front)
                let file = CrossMedia.Current.TakePhotoAsync(mediaOptions) |> Async.AwaitTask |> Async.RunSynchronously
                viewModel.Stream <- (checkNull file).GetStream()

这是 C# 的等价物:

takePhoto.Clicked += async (sender, args) =>
{
     if (!CrossMedia.Current.IsCameraAvailable || !CrossMedia.Current.IsTakePhotoSupported)
        return;

     var file = await CrossMedia.Current.TakePhotoAsync(new Plugin.Media.Abstractions.StoreCameraMediaOptions
     {
        Directory = "Test",
        SaveToAlbum = true,
        CompressionQuality = 75,
        CustomPhotoSize = 50,
        PhotoSize = PhotoSize.MaxWidthHeight,
        MaxWidthHeight = 2000,
        DefaultCamera = CameraDevice.Front
     });

     if (file == null)
        return;

     viewModel.Stream = file.GetStream();
};

我知道我没有在功能齐全的方面使用 F#,但是我缺少什么让它工作,或者在 F# 中使用 C# 任务时我没有意识到一些限制?

我尝试使用async {} 表达式,它在单击按钮时没有任何作用,但我认为这是将TakePhotoAsync 放在后台线程上,而它可能必须在主线程上运行。我现在似乎通​​过显示权限对话框并最终显示一个黑屏就像相机正在启动一样工作,但它卡在那里并且从未真正显示相机供稿。

【问题讨论】:

  • 如果您不以任何方式将此问题与 Xamarin 和 C# 相关联,也许您会得到答案。任务不是特定于 C# 的 - 它们也在 F# 中使用。您的问题似乎不是 Xamarin 特有的。

标签: xamarin.forms f# c#-to-f#


【解决方案1】:

我怀疑这里的问题是您使用 RunSynchronously 拍摄的照片可能没有达到您的预期。您可以将RunSynchronously 想象成在.Net 任务对象上调用.Result。因此,作为一种猜测,这会导致 Xamarin 标准库中某处出现死锁。

相反,如果您希望您的 F# 代码更接近您的 C# 代码,那么您将需要类似以下内容:

member this.TakePhoto(sender: obj, e : EventArgs) =
    async {
       if not CrossMedia.Current.IsCameraAvailable ... then return ()
       else
           let! file = CrossMedia.Current.TakePhotoAsync(Plugin.Media.Abstractions.StoreCameraMediaOptions(
               Directory = "Test",
               SaveToAlbum = true,
               CompressionQuality = 75,
               CustomPhotoSize = 50,
               PhotoSize = PhotoSize.MaxWidthHeight,
               MaxWidthHeight = 2000,
               DefaultCamera = CameraDevice.Front)) |> Async.AwaitTask

           if isNull file then return ()
           else
               viewModel.Stream <- file.GetStream()
    } |> Async.StartAsTask

如果您愿意依赖外部库,也可以将异步块替换为任务计算表达式

【讨论】:

  • 这就像我之前尝试过的async {} 设置,或者当我尝试使用FSharp.Control.Tasks.V2.task {} 时,它在单击按钮时不会执行任何操作。我什至能够获得提示权限对话框的唯一方法是使用Async.RunSynchronously,它卡在同一点,就像我将C#版本切换为使用WaitResult一样,所以我接受它@ F# 中的 987654330@,不是 100% 等同于 C# await
  • 您能否更新您的问题以显示您的TakePhoto 方法是如何被调用的?看起来你的 C# 方法是一个按钮单击事件处理程序,但是我没有从那里看到的任何东西调用你的 F# 方法
  • 按钮点击事件正在 F# 端的 Xaml 中设置
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-01-06
  • 1970-01-01
  • 2012-08-09
相关资源
最近更新 更多