首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >F# Async<_> to Async<obj>

F# Async<_> to Async<obj>
EN

Stack Overflow用户
提问于 2021-12-31 05:23:55
回答 1查看 88关注 0票数 2

我正在与F#合作开发PowerShell工具。我目前遇到了一个块,因为Async<_>是一个泛型类型,它不是从非泛型类型派生出来的,所以我不能请求一个Async<_>Async作为参数值--我必须指定确切的泛型类型参数。

(对于那些不熟悉这两种语言之间的交互的人,我可以用.NET语言(如F# )编写一个类,从PowerShell库中的一个类派生它,并给它一个特定的属性,并且当我运行PowerShell和导入我的库时,我的类会被公开为一个命令。命令类型不能是泛型的。类型的属性公开为PowerShell参数。)

据我所知,我不能通过在非泛型类型上拥有一个泛型成员来避免这种情况,所以理想情况下,我应该有一个转换属性(对于非PS用户,转换属性在运行时参数绑定期间有效地执行类型转换)来将Async<_>转换为Async<obj>。在大多数情况下,这对我来说会很好。但是,我无法找到一种检查值是否为Async<_>的方法,因为编译时的check computation :? Async<_>最终会变成computation :? Async<obj>,不幸的是,这与computation :? Async<obj>不一样,在传递Async<int>时返回false。

我在C#中遇到了类似的问题,在运行反射测试并使参数成为派生的基类型System.Threading.Tasks.Task之后,能够利用dynamic关键字。

代码语言:javascript
复制
const BindingFlags flags = BindingFlags.Public | BindingFlags.Instance | BindingFlags.FlattenHeirarchy;
var isTaskOf = task.GetType()
    .GetProperty("GetAwaiter", flags)
    .PropertyType
    .GetMethod("GetResult", flags)
    .ReturnType != typeof(void);
if (isTaskOf) {
  var result = await (dynamic)task;
}

如果可能的话,我愿意在F#中这样做,但是:

  • 我无法成功地使dynamic lookup operator ?编译。具体来说,“没有一个类型‘异步<’a>,字符串‘支持'?’操作符”。我不知道我做错了什么,因为解释看起来很简单,我找不到关于这个消息的任何其他报告,也找不到这个操作符的要求。
  • 我不知道这是否有效,或者那个操作符是否只用于动态访问对象的一个成员。

我尝试过的解决办法是:

代码语言:javascript
复制
/// Transform from Async<_> to Async<obj>
override _.Transform(_, item : obj) : obj =
  match item with
  // only matches Async<obj>. I get a compiler warning that _ is constrained to obj
  | :? Async<_> as computation ->
    let boxedComputation : Async<obj> = async { return! computation }
    boxedComputation
  // if the value is not an async computation, let it pass through. This will allow other transformation or type converters to try to convert the value
  | _ -> item

override _.Transform(_, item) =
  // no compiler warning about the type being constrained to obj, but the if test does not pass unless item is Async<obj>
  if (item :? Async<_>) then async { return! item :?> Async<_> }
  else item

另一件事是完全使用反射--获取异步类型,以反射方式调用所有AsyncBuilder方法来创建计算表达式,然后将其转换为异步。由于我对F#相当陌生,所以我不确定我能把这样的计算表达式拼凑起来有多好,而且不管是哪种方式,它看起来都比它应该要复杂得多。我希望有更好的方法来识别异步计算的返回类型和/或只是将结果装箱,而不关心它的实际类型。

在尝试了一些非常复杂的东西之后,使用反射和AsyncBuilder类型进行编辑后,我意识到我可以更简单地利用它。这是我目前的工作解决方案,但我仍然在寻找更好的选择。

代码语言:javascript
复制
    static let boxAsyncReturnValue v = async { return v :> obj }
    static let bindFunctionReflected = typeof<FSharpAsyncObjTransformationAttribute>.GetMethod(
        nameof boxAsyncReturnValue,
        BindingFlags.NonPublic ||| BindingFlags.Static
        )

    override _.Transform(engineIntrinsics, item) =
        // I need to identify the current return type of the computation, and quit if "item" is not Async<_>
        if item = null then item else
        let itemType = item.GetType()
        if not itemType.IsGenericType then item else
        let genericItemType = itemType.GetGenericTypeDefinition()
        if genericItemType <> typedefof<Async<_>> then item else
        let returnType = itemType.GetGenericArguments()[0]
        if returnType = typeof<obj> then item else
        bindFunctionReflected.MakeGenericMethod(itemType).Invoke(null, [|item|])
EN

回答 1

Stack Overflow用户

发布于 2021-12-31 06:07:32

我就是这样做的:

代码语言:javascript
复制
let convert (a: Async<_>) =
    async {
        let! x = a
        return box x
    }

在编译时,它的行为与您预期的一样:

代码语言:javascript
复制
let a = async { return "hello" }

let o: Async<obj> = convert a

let res = Async.RunSynchronously o

printfn "%s" res // Error: expected type 'string' but is type 'obj'
printfn "%s" (unbox<string> res) // compiles, prints the string
票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/70539260

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档