【问题标题】:F#: How to Access a Dotnet Object's Method's Specific Overload as a First Class-objectF#:如何将 Dotnet 对象的方法的特定重载作为第一类对象访问
【发布时间】:2021-04-16 09:30:04
【问题描述】:

我可以做到(F# FSI):

let o = Object()
let m = o.GetHashCode;; //[1]
val o : Object
val m : (unit -> int)

这使得方法 GetHashCode 可以作为绑定 m 的一流函数访问和调用:

m ();;
val it : int = 12345678

如果有问题的方法存在重载,我将如何执行此操作,比如额外的Object.GetHashCode(int whateverParameter)?我现在只能得到“第一个”(最短的?)方法。我可以在调用1 中提供额外的参数类型信息来指定我想要的重载吗?

编辑

感谢您迄今为止的意见。类型注释似乎是要走的路。尽管如此,对于我更复杂的示例,我仍然无法弄清楚如何正确地做到这一点。

这是我正在处理的类型的具体方法签名(它来自 octokit.net 库,因此您的 fsi 中需要类似 #r "nuget:include=Octokit" 的东西 [with /langversion:preview])。

我有兴趣检索GetAllForRepository 的特定重载,顺便提一下,列出的第一个:GetAllForRepository(string, string)。

open Octokit
let client = GitHubClient(...)
client.Issue.GetType().GetMethods()
|> Seq.filter (fun methodInfo -> methodInfo.Name.StartsWith("GetAllForRepository"))
|> Seq.map (fun methodInfo -> (methodInfo.GetParameters(), methodInfo.ReturnType))
|> Seq.map (fun (parameterInfos, returnType) ->
    (parameterInfos |> Seq.map (fun parameterInfo -> parameterInfo.ParameterType.FullName), returnType.FullName))

val it : seq<seq<string> * string> =
  seq
    [(seq ["System.String"; "System.String"],
      "System.Threading.Tasks.Task`1[[System.Collections.Generic.IReadOnlyList`1[[Octokit.Issue, Octokit, Version=0.48.0.0, Culture=neutral, PublicKeyToken=null]], System.Private.CoreLib, Version=5.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]]");
     (seq ["System.Int64"],
      "System.Threading.Tasks.Task`1[[System.Collections.Generic.IReadOnlyList`1[[Octokit.Issue, Octokit, Version=0.48.0.0, Culture=neutral, PublicKeyToken=null]], System.Private.CoreLib, Version=5.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]]");
     (seq ["System.String"; "System.String"; "Octokit.ApiOptions"],
      "System.Threading.Tasks.Task`1[[System.Collections.Generic.IReadOnlyList`1[[Octokit.Issue, Octokit, Version=0.48.0.0, Culture=neutral, PublicKeyToken=null]], System.Private.CoreLib, Version=5.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]]");
     (seq ["System.Int64"; "Octokit.ApiOptions"],
      "System.Threading.Tasks.Task`1[[System.Collections.Generic.IReadOnlyList`1[[Octokit.Issue, Octokit, Version=0.48.0.0, Culture=neutral, PublicKeyToken=null]], System.Private.CoreLib, Version=5.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]]");
     (seq ["System.String"; "System.String"; "Octokit.RepositoryIssueRequest"],
      "System.Threading.Tasks.Task`1[[System.Collections.Generic.IReadOnlyList`1[[Octokit.Issue, Octokit, Version=0.48.0.0, Culture=neutral, PublicKeyToken=null]], System.Private.CoreLib, Version=5.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]]");
     (seq ["System.Int64"; "Octokit.RepositoryIssueRequest"],
      "System.Threading.Tasks.Task`1[[System.Collections.Generic.IReadOnlyList`1[[Octokit.Issue, Octokit, Version=0.48.0.0, Culture=neutral, PublicKeyToken=null]], System.Private.CoreLib, Version=5.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]]");
     (seq
        ["System.String"; "System.String"; "Octokit.RepositoryIssueRequest";
         "Octokit.ApiOptions"],
      "System.Threading.Tasks.Task`1[[System.Collections.Generic.IReadOnlyList`1[[Octokit.Issue, Octokit, Version=0.48.0.0, Culture=neutral, PublicKeyToken=null]], System.Private.CoreLib, Version=5.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]]");
     (seq
        ["System.Int64"; "Octokit.RepositoryIssueRequest";
         "Octokit.ApiOptions"],
      "System.Threading.Tasks.Task`1[[System.Collections.Generic.IReadOnlyList`1[[Octokit.Issue, Octokit, Version=0.48.0.0, Culture=neutral, PublicKeyToken=null]], System.Private.CoreLib, Version=5.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]]")]

现在,“最短”签名需要一个 int64 参数:

let m = client.Issue.GetAllForRepository;;
val m :
  (int64 ->
     System.Threading.Tasks.Task<System.Collections.Generic.IReadOnlyList<Octokit.Issue>>)

我可以显式声明签名并接收相同的方法:

let m: (int64 -> System.Threading.Tasks.Task<System.Collections.Generic.IReadOnlyList<Octokit.Issue>>) = client.Issue.GetAllForRepository;;
val m :
  (int64 ->
     System.Threading.Tasks.Task<System.Collections.Generic.IReadOnlyList<Octokit.Issue>>)

但是访问(string, string) 版本给了我:

let m: (string -> string -> System.Threading.Tasks.Task<System.Collections.Generic.IReadOnlyList<Octokit.Issue>>) = client.Issue.GetAllForRepository;;

  let m: (string -> string -> System.Threading.Tasks.Task<System.Collections.Generic.IReadOnlyList<Octokit.Issue>>) = client.Issue.GetAllForRepository;;
  --------------------------------------------------------------------------------------------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

stdin(33,117): error FS0193: Type constraint mismatch. The type
    'string'
is not compatible with type
    'int64'

尝试访问具有多个参数的任何其他重载时会发生类似错误:

let m: (int64 -> Octokit.RepositoryIssueRequest -> System.Threading.Tasks.Task<System.Collections.Generic.IReadOnlyList<Octokit.Issue>>) = client.Issue.GetAllForRepository;;

  let m: (int64 -> Octokit.RepositoryIssueRequest -> System.Threading.Tasks.Task<System.Collections.Generic.IReadOnlyList<Octokit.Issue>>) = client.Issue.GetAllForRepository;;
  -------------------------------------------------------------------------------------------------------------------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

stdin(37,140): error FS0001: This expression was expected to have type
    'Octokit.RepositoryIssueRequest -> System.Threading.Tasks.Task<System.Collections.Generic.IReadOnlyList<Octokit.Issue>>'
but here has type
    'System.Threading.Tasks.Task<System.Collections.Generic.IReadOnlyList<Octokit.Issue>>'
let m: (int64 -> Octokit.RepositoryIssueRequest -> Octokit.ApiOptions -> System.Threading.Tasks.Task<System.Collections.Generic.IReadOnlyList<Octokit.Issue>>) = client.Issue.- GetAllForRepository;;

  let m: (int64 -> Octokit.RepositoryIssueRequest -> Octokit.ApiOptions -> System.Threading.Tasks.Task<System.Collections.Generic.IReadOnlyList<Octokit.Issue>>) = client.Issue.GetAllForRepository;;
  -----------------------------------------------------------------------------------------------------------------------------------------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

stdin(36,162): error FS0001: This expression was expected to have type
    'Octokit.RepositoryIssueRequest -> Octokit.ApiOptions -> System.Threading.Tasks.Task<System.Collections.Generic.IReadOnlyList<Octokit.Issue>>'
but here has type
    'System.Threading.Tasks.Task<System.Collections.Generic.IReadOnlyList<Octokit.Issue>>'

那么,我在类型注释方面做错了吗?

【问题讨论】:

  • 当我尝试将具有重载的方法分配给变量时,我收到建议“可能需要类型注释。” - 您是否尝试这样做?
  • @Christian 欢迎来到 F# 社区!
  • 这确实适用于简单的情况,例如let m: char -&gt; int = "Hello".IndexOf;;,选择System.String.IndexOf(char) 而不是System.String.IndexOf(string)。但是我想要的方法的签名(来自 octokit.net 库)更复杂,比如Task&lt;IReadOnlyList&lt;Issue&gt;&gt; GetAllForRepository(string owner, string name)。可能是因为 Task&lt;...&gt; 返回值。
  • 你试过m: ((string * string) -&gt; System.Threading.Tasks.Task&lt;System.Collections.Generic.IReadOnlyList&lt;Octokit.Issue&gt;&gt;)吗?

标签: function f# overloading first-class


【解决方案1】:

是的,类型注释将允许您选择所需的方法覆盖:

type MyClass() =
    member __.MyMethod() = 1
    member __.MyMethod(x : int) = 2

let o = MyClass()
let m1 : (unit -> int) = o.MyMethod
let m2 : (int -> int) = o.MyMethod
printfn "%A" <| m1 ()
printfn "%A" <| m2 0

【讨论】:

  • 谢谢@brianberns。这适用于“简单”案例,不幸的是不适用于我的真实示例。我已经扩展了原来的问题。
【解决方案2】:

响应您的编辑,该方法的输入是 string * string,而不是 string -&gt; string(即 uncurried 与 curried),所以我认为这会起作用:

let m : (string * string -> _) = client.Issue.GetAllForRepository

【讨论】:

  • 非常感谢大家,特别是 @brianberns 帮助我解决了我的具体愚蠢问题!
  • 一点也不傻。柯里化需要一段时间才能习惯。乐于助人。
猜你喜欢
  • 2016-06-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-12-10
  • 1970-01-01
  • 2013-10-06
  • 2022-01-05
相关资源
最近更新 更多