powershell - F# 异步<_> 到异步<obj>

标签 powershell generics f#

我正在使用 F# 开发 PowerShell 工具。我目前遇到了障碍,因为 Async<_>是不是从非泛型类型派生的泛型类型,因此我无法请求 Async<_>Async作为参数值 - 我必须指定确切的泛型类型参数。

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

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

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

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 ? 编译。具体来说,“'Async<'a>, string' 类型都不支持 '?'运算符(operator)”。不确定我做错了什么,因为解释看起来很简单,而且我找不到此消息的任何其他报告或该运算符(operator)的要求。
  • 我不知道这是否可行,或者该运算符是否仅用于动态访问对象的成员。

我尝试过的解决方案是:

/// 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 类型的反射进行一些极其复杂的事情之后,我意识到我可以更简单地利用它。这是我当前的工作解决方案,但我仍在寻找更好的选择。


    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|])

最佳答案

这就是我要做的:

let convert (a: Async<_>) =
    async {
        let! x = a
        return box x
    }

在编译时它的行为正如您所期望的:

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

关于powershell - F# 异步<_> 到异步<obj>,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/70539260/

相关文章:

powershell - 网站集还原后,SharePoint用户权限令人头疼

具有类和嵌套静态接口(interface)的 Java 泛型

.net - 可区分联合中的函数约束泛型参数的类型

f# - 禁止在 F# 交互中显示变量的内容

powershell - 在 powershell 中安装永久驱动器

vba - Wscript.Shell Exec 中的单词 "encode"确实会阻止 powershell 脚本运行。我能做什么?

powershell - 如何在Powershell中优化多个if语句?

java - 在 Java 中实现通用接口(interface)

java - Cucumber-jvm 和页面对象的泛型不起作用

.net - 在 Mac 上从命令行运行 Dotnet F Sharp 编译器