Comparing F# with C#: Downloading a web page
https://swlaschin.gitbooks.io/fsharpforfunandprofit/content/posts/fvsc-download.html
在此示例中,我们将比较用于下载网页的 F# 和 C# 代码,以及用于处理文本流的回调。
我们将从简单的 F# 实现开始。
// "open" brings a .NET namespace into visibility
open System.Net
open System
open System.IO
// Fetch the contents of a web page
let fetchUrl callback url =
let req = WebRequest.Create(Uri(url))
use resp = req.GetResponse()
use stream = resp.GetResponseStream()
use reader = new IO.StreamReader(stream)
callback reader url让我们看一下这段代码:
在顶部使用“
open”允许我们编写“WebRequest”而不是“System.Net.WebRequest”。它类似于 C# 中的“using System.Net”。接下来,我们定义
fetchUrl函数,它有两个参数,一个处理流的回调,以及要获取的url。接下来我们将
url字符串包装在Uri中。 F# 具有严格的类型检查,所以如果我们改为编写:let req = WebRequest.Create(url)编译器会抱怨它不知道要使用哪个版本的WebRequest.Create。在声明响应、流和阅读器值时,使用“
use”关键字而不是“let”。这只能与实现IDisposable的类结合使用。它告诉编译器在超出范围时自动处理资源。这相当于 C# 中的“using”关键字。最后一行以
StreamReader和url作为参数调用回调函数。请注意,不必在任何地方指定回调的类型。
现在这里是等效的 C# 实现。
class WebPageDownloader
{
public TResult FetchUrl<TResult>(
string url,
Func<string, StreamReader, TResult> callback)
{
var req = WebRequest.Create(url);
using (var resp = req.GetResponse())
{
using (var stream = resp.GetResponseStream())
{
using (var reader = new StreamReader(stream))
{
return callback(url, reader);
}
}
}
}
}像往常一样,C# 版本有更多的“噪音”。
仅花括号就有 10 行,并且有 5 层嵌套的视觉复杂性*
所有参数类型都必须显式声明,并且泛型
TResult类型必须重复三次。的确,在这个特定示例中,当所有 using 语句都相邻时,可以删除额外的大括号和缩进,但在更一般的情况下,它们是需要的。
测试代码
回到 F# 领域,我们现在可以交互式地测试代码:
let myCallback (reader:IO.StreamReader) url =
let html = reader.ReadToEnd()
let html1000 = html.Substring(0,1000)
printfn "Downloaded %s. First 1000 is %s" url html1000
html // return all the html
//test
let google = fetchUrl myCallback "http://google.com"最后,我们必须显式进行 reader 参数的类型声明 (reader:IO.StreamReader)。这是必需的,因为 F# 编译器无法自动确定“reader”参数的类型。
F# 的一个非常有用的功能是您可以在函数中“嵌入”参数,这样就不必每次都传入它们。这就是为什么将 url 参数放在最后而不是第一个,就像在 C# 版本中一样。回调可以设置一次,而 url 因调用而异。【也就是柯里化】
// build a function with the callback "baked in"
let fetchUrl2 = fetchUrl myCallback
// test
let google = fetchUrl2 "http://www.google.com"
let bbc = fetchUrl2 "http://news.bbc.co.uk"
// test with a list of sites
let sites = ["http://www.bing.com";
"http://www.google.com";
"http://www.yahoo.com"]
// process each site in the list
sites |> List.map fetchUrl2最后一行(使用 List.map)显示了如何轻松地将新函数与列表处理函数结合使用以一次下载整个列表。
这是等效的 C# 代码:
[Test]
public void TestFetchUrlWithCallback()
{
Func<string, StreamReader, string> myCallback = (url, reader) =>
{
var html = reader.ReadToEnd();
var html1000 = html.Substring(0, 1000);
Console.WriteLine(
"Downloaded {0}. First 1000 is {1}", url,
html1000);
return html;
};
var downloader = new WebPageDownloader();
var google = downloader.FetchUrl("http://www.google.com",
myCallback);
// test with a list of sites
var sites = new List<string> {
"http://www.bing.com",
"http://www.google.com",
"http://www.yahoo.com"};
// process each site in the list
sites.ForEach(site => downloader.FetchUrl(site, myCallback));
}同样,这段代码比F#的代码更嘈杂一些,有许多显式类型引用。更重要的是,C#代码不容易让你在函数中嵌入一些参数,所以回调必须每次都明确引用。
Last updated