我正在与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关键字。
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#中这样做,但是:
?编译。具体来说,“没有一个类型‘异步<’a>,字符串‘支持'?’操作符”。我不知道我做错了什么,因为解释看起来很简单,我找不到关于这个消息的任何其他报告,也找不到这个操作符的要求。我尝试过的解决办法是:
/// 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|])发布于 2021-12-31 06:07:32
我就是这样做的:
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 stringhttps://stackoverflow.com/questions/70539260
复制相似问题