代码之家  ›  专栏  ›  技术社区  ›  Kurt Schelfthout

编写.NET互操作函数的惯用方法

  •  6
  • Kurt Schelfthout  · 技术社区  · 14 年前

    如果可能的话,我正在寻找一种更惯用的方法来编写以下clojure代码:

    (import '(System.Net HttpWebRequest NetworkCredential)
            '(System.IO StreamReader)) 
    
    (defn downloadWebPage
      "Downloads the webpage at the given url and returns its contents."
      [^String url ^String user ^String password]
      (def req (HttpWebRequest/Create url))
      (.set_Credentials req (NetworkCredential. user password ""))
      (.set_UserAgent req ".NET")
      (def res (.GetResponse req))
      (def responsestr (.GetResponseStream res))
      (def rdr (StreamReader. responsestr))
      (def content (.ReadToEnd rdr))
      (.Close rdr)
      (.Close responsestr)
      (.Close res)
      content
      )
    

    这是在ClojureCLR和作品。(事实上,它是CLR变体并不重要)

    我想去掉defs(换成let?他们能互相提及吗?)

    去小溪的更好的方法是什么?记住。。链接不起作用,因为我以后需要关闭流。

    编辑:回答之后,我在.NET中找到了一种使用WebClient类下载网页的更简单的方法。我仍然使用了Michal推荐的许多方法-只是想记录下我现在认为是最好的答案:

    (defn download-web-page
        "Downloads the webpage at the given url and returns its contents."
        [^String url ^String user ^String password]
        (with-open [client  (doto (WebClient.)
                            (.set_Credentials (NetworkCredential. user password "")))]
          (.DownloadString client url)))
    
    1 回复  |  直到 14 年前
        1
  •  6
  •   Community CDub    5 年前

    问题中的代码可以按照这样的习惯性方式重新编写(以任何打字错误为模——这里希望没有):

    (defn download-web-page
      "Downloads the webpage at the given url and returns its contents."
      [^String url ^String user ^String password]
      (let [req (doto (HttpWebRequest/Create url)
                  (.set_Credentials (NetworkCredential. user password ""))
                  (.set_UserAgent ".NET"))
            response        (.GetResponse req)
            response-stream (.GetResponseStream res)
            rdr             (StreamReader. response-stream)
            content (.ReadToEnd rdr)]
        (.Close rdr)
        (.Close response-stream)
        (.Close response)
        content))
    

    with-open 电话 .Close .readToEnd 急切地消耗整个流,这可以进一步简化为

    更新: 打开 .Dispose 在绑定对象上。如果这可以代替 ,很好;如果 .关闭 是必需的,您可以编写自己的版本 打开 使用 .关闭 而不是(可能复制 the original ):

    (defn download-web-page
      "Downloads the webpage at the given url and returns its contents."
      [^String url ^String user ^String password]
      (let [req (doto (HttpWebRequest/Create url)
                  (.set_Credentials (NetworkCredential. user password ""))
                  (.set_UserAgent ".NET"))]
        (with-open [response        (.GetResponse req)
                    response-stream (.GetResponseStream res)
                    rdr             (StreamReader. response-stream)]
          (.ReadToEnd rdr))))
    

    一些评论:

    1. 不要使用 def defn 除非你真的知道你需要这样做。(实际上是在顶层中立即使用它们 let 如果需要创建的对象关闭 -绑定本地人。。。有什么比这更奇怪的吗 仔细检查!)

      定义 &Co.创建顶级变量或重置其根绑定;在程序的常规操作过程中这样做完全违背Clojure的功能精神。也许更重要的是,从一个实际的角度来看,任何依赖于“拥有”一堆变量的函数一次只能由一个线程执行;这是没有理由的 download-web-page 因此应该受到限制。

    2. -引入的绑定可能不是相互递归的;后面的绑定可能引用前面的绑定,但不是相反。相互递归的局部函数可以用 letfn 很好用。

    推荐文章